How to use 'Decimal' as a field type in GraphQL schema? - node.js

I want to use Decimal as a type for latitude & longitude field in my GraphGL schema. But GraphQL provides only Float and Int.
Is there is any package to solve this?

I looked at the source code for this module can came up with this:
/// I M P O R T
import Big from "big.js";
import { GraphQLScalarType, Kind } from "graphql";
/// E X P O R T
export default new GraphQLScalarType({
name: "Decimal",
description: "The `Decimal` scalar type to represent currency values",
serialize(value) {
return new Big(value);
},
parseLiteral(ast) {
if (ast.kind !== Kind.STRING) {
// #ts-ignore | TS2339
throw new TypeError(`${String(ast.value)} is not a valid decimal value.`);
}
return Big(ast.value);
},
parseValue(value) {
return Big(value);
}
});
Then, I add the reference to my resolver so I can just add Decimal to my SDL code.

Related

Formik initial value partially undefined to result type

with a type icecream i have this model.
enum Sugar {
High = 1,
Medium = 2,
Low = 3,
}
Type IceCream = {
Name:string;
SugarContent: Sodium
}
now, in my formik form i wanna create a form with initial values, where sugarcontent is undefined. But it has to be set in the form according to my validiationschema (yup)
sugar: yup
.number()
.required("Sugar content is required")
.min(1)
.max(3)
is it possible to get a correctly typed form output for this? Something like the following as an idea
Type FormIceCream = {
Name:string;
SugarContent?: Sugar
}
const InitialValues:FormIceCream = {
Name:"",
SugarContent:undefined
}
return <Formik<IceCream>
initialValues={InitialValues} // <-- warning, Type 'undefined' is not assignable to type 'SugarContent'
validationSchema={IceCreamValidation}
onSubmit={(values:IceCream) => console.log(values)}
> ...

Typescript, Enums with strings and numbers

I have an interface with
interface mathTest {
mathAction: MathActionEnum;
}
The reason for this is that I want this property to have just one of the specific values from the enum below.
enum MathActionEnum {
'byOne' = 1,
'byTwo' = 2,
'byFour' = 4,
'byEight' = 8,
}
Assume mathAction = 'byOne' -> received from an API response.
First scenario: doing an arithmetic operation, I need the number value: let result: number = amount / MathActionEnum[mathAction] but I get an error:
The right-hand side of an arithmetic operation must be of type 'any',
'number', 'bigint' or an enum type
It is a number but still I need to cast it with Number(MathActionEnum[mathAction]) for the error to go away.
Second scenario: equality check, I need the string value: if (mathAction === MathActionEnum[MathActionEnum.byOne]) but I get an error:
This condition will always return 'false' since the types
'MathActionEnum' and 'string' have no overlap
Which makes sense.
I'm a bit lost, is there a way to syntax it as I expect it to be? Maybe I need to define things differently?
Thanks
TypeScript enums are absolutely NOT suitable for any sort of key-value mapping. The intent is to have a grouping of uniquely identifiable labels, but labels are where it ends. While they may indeed have a number representation under the hood, they are not intended for use as a key-value store. You will have to cast it to "extract the number", and then the type is just number, so you effectively defeat the purpose of enums.
For all intents and purposes, think of them like keys with no useful values:
const MathActionEnum = Object.freeze({
byOne: Symbol(),
byTwo: Symbol(),
byFour: Symbol(),
byEight: Symbol(),
})
Consider the newer alternative, const assertion, instead. They'll provide you with type safety on both keys and values:
const MathActions = {
'byOne': 1,
'byTwo': 2,
'byFour': 4,
'byEight': 8,
} as const
type MathAction = keyof typeof MathActions
type MathActionValue = typeof MathActions[MathAction]
You get full type safety on both keys and values:
const example = (action: MathAction) => {
return 2 * MathActions[action]
}
example('byOne')
// compile error, not a valid key
example('foo')
// -------------
const example2 = (actionValue: MathActionValue) => {
return 2 * actionValue
}
example2(4)
// compile error, not a valid value
example2(19)
You can even add type assertions to check if arbitrary values are a key or value:
const isAction = (action: string): action is MathAction => {
return Object.keys(MathActions).includes(action)
}
isAction
const isActionValue = (actionValue: number): actionValue is MathActionValue => {
return Object.values(MathActions).includes(actionValue as any)
}
You'll even get IDE autocompletion for both keys and values:
Here's a Playground

interfaces in typescript: use function parameter on a nested object reference

I have this object model:
export interface UpdateDocument {
updated_at?: string;
actions?: Actions;
}
export interface Actions {
update?: Update;
create?: Create;
}
export interface Update {
name?: Values;
priority?: Values;
engine?: Values;
fact?: Fact;
}
export interface Fact {
brand?: Values;
model?: Values;
version?: Values;
year?: Values;
km?: Values;
color?: Values;
}
export interface Values {
old?: any;
new?: any;
}
export interface Create {
conditions?: Object;
recipe?: Object;
}
In this function i tried to pass a parameter to references an objects field and do an assignment:
async buildUpdateDocument (updateDocument: UpdateDocument) {
let fields: Array<string> = ['name','priority','engine','fact','adjustment'];
fields.forEach((field: string) =>{
updateDocument.actions!.update![field]!.old = await this.getValue(field)
})
}
but i hav this ts-error: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Update'.
No index signature with a parameter of type 'string' was found on type 'Update'.ts(7053)
How can i pass the parameter in this kind of reference to do the assignment?
First of you have specified a wrong key adjustment that doesn't exist on Update. This example uses a explicit type (as const):
let fields = ['name','priority','engine','fact'] as const;
Make sure to not add a type definition to the variable when using as const.
Here is the modified function to better fit TS standards. This also addresses the forEach-async problem in the original code. The real correct structure would be null checks for each of the x | undefined types, but to override the type errors the following is the way to go.
async function buildUpdateDocument (updateDocument: UpdateDocument) {
const fields: Array<keyof Update> = ['name','priority','engine','fact'];
await Promise.all(fields.map(async (field) => {
(((updateDocument.actions as {update: Update}).update)[field] as Values).old = await this.getValue(field);
}));
}
Your current code has bugs that the type system would help you find if you let it. First, the adjustment field doesn't exist on the Update type, and old field doesn't exist on the Fact type.
To implement this properly, I would use a Record for the data type instead:
const updateFields = ['name', 'priority', 'engine', 'fact'] as const
export type UpdateFields = typeof updateFields[number]
export type Update = Record<UpdateFields, Values>
And then, your function will look like this:
async buildUpdateDocument (updateDocument: UpdateDocument) {
updateFields.forEach((field) =>{
updateDocument.actions!.update![field]!.old = await this.getValue(field)
})
}

Iterate on string enum

I must be missing something, but I found several ways to iterate through an Enum but not on a string enum.
The following enum is given:
export enum Locales {
En = 'en',
Fr = 'fr',
De = 'de',
Es = 'es',
It = 'it',
Nl = 'nl',
No = 'no',
Tr = 'tr',
}
What I want to achieve:
I want to iterate on that string enum so that I get the values (!). What I've tried:
for (const key of Object.keys(Locales)) {
const locale: string = Locales[key];
console.log(locale); // Should print 'en', 'fr' and so on
}
The problem with above code:
Due to the strict tsconfig (which doesn't allow implicit anys) I can not compile this to javascript. Since this is not my project I can not change this tsconfig either. It highlights the key variable at Locales[key] and the error makes sense to me:
[ts] Element implicitly has an 'any' type because index expression is
not of type 'number'.
The question:
What's the proper way iterating through a string enum to get it's values with Typescript 2.6+?
As betadeveloper suggested, you can get proper type for key if you use type assertion as keyof typeof Locales. Or you can wrap it in type-safe variant of Object.keys() function like this:
export enum Locales {
En = 'en',
Fr = 'fr',
De = 'de',
Es = 'es',
It = 'it',
Nl = 'nl',
No = 'no',
Tr = 'tr',
}
function enumKeys<E>(e: E): (keyof E)[] {
return Object.keys(e) as (keyof E)[];
}
for (const key of enumKeys(Locales)) {
const locale: string = Locales[key];
console.log(locale);
}
Also, for the record, old-style for .. in loop still works:
for (let key in Locales) {
let locale = Locales[key];
console.log(locale);
}
#Artem and #betadeveloper pointed out that I can use the keyof typeof Locales type for my approach. The solution I eventually came up with looks like this:
const keys: (keyof typeof Locales)[] = <(keyof typeof Locales)[]>Object.keys(Locales);
for (const key of keys) {
const locale: string = Locales[key];
console.log(locale); // Prints 'en', 'fr' and so on
}
Lodash
Lodash is a good option to use since it is easy to use and provides a easy to understand api. From the lodash methods, forIn is the option you're looking for. To get typescript declaration files, you can install:
npm install #types/lodash
With the forIn method, you get the value and the key of Locales object.
import { forIn } from 'lodash'
enum Locales {
En = 'en',
Fr = 'fr'
// ...
}
forIn(Locales, (value, key) => console.log(value, key))
Adding to this question because I was looking for a solution.
The best solution is to use Object.values() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values
Object.values(Locales).forEach((locale) => console.log(locale));
// or
for (const locale of Object.values(Locales)) {
console.log(locale);
}
Also consider Object.entries() to get both the keys and values of a string value enum.
const entries = Object.entries(Locales);
let LocalesArray= [];
entries.forEach((type) => {
LocalesArray.push({ key: type[0], name: type[1] });
});
Simple way to iterate over enum values and convert them into object array.

TypeScript warning => TS7017: Index signature of object type implicitly has any type

I am getting the following TypeScript warning -
Index signature of object type implicitly has any type
Here is the code that cases the warning:
Object.keys(events).forEach(function (k: string) {
const ev: ISumanEvent = events[k]; // warning is for this line!!
const toStr = String(ev);
assert(ev.explanation.length > 20, ' => (collapsed).');
if (toStr !== k) {
throw new Error(' => (collapsed).');
}
});
can anyone determine from this code block why the warning shows up? I cannot figure it out.
If it helps this is the definition for ISumanEvent:
interface ISumanEvent extends String {
explanation: string,
toString: TSumanToString
}
You could add an indexer property to your interface definition:
interface ISumanEvent extends String {
explanation: string,
toString: TSumanToString,
[key: string]: string|TSumanToString|ISumanEvent;
}
which will allow you to access it by index as you do: events[k];. Also with union indexer it's better to let the compiler infer the type instead of explicitly defining it:
const ev = events[k];

Resources