'Typescript function attribute inference
I try to make a strongly typed form generator and in order to configurate it, I need some options based on model. I would like to know if it's possible to infer return type of a fonction from it arguments, probably I missed some typescript basics on inference.
I made a simple, synthetic ( and useless ) example to illustrate my purpose:
// Option type that take for keys an OptionName and return fields based on Model
type Options<Model extends Record<string, any>, OptionName extends string = string> = {
[key in OptionName]: {
fields: {
[field in keyof Model]?: {
label: string;
};
};
};
};
// Here a basic function who return options and expect getting return type based on options
function useOptions<Model extends Record<string, any>>(options: Options<Model>): Options<Model, keyof typeof options> {
return options as Options<Model, keyof typeof options>;
}
And something like that to use it:
type Person = {
lastname: string;
firstname: string;
country: string;
};
const options: Options<Person> = {
ContactFields: {
fields: {
lastname: {
label: 'Lastname',
},
firstname: {
label: 'Firstname',
},
},
},
AddressFields: {
fields: {
country: {
label: 'Country',
},
},
},
};
// Does not return type error
const { ContactFields, AddressFields } = useOptions<Person>(options);
// Does not return type error
// Should return type errors since return didn't match options
const { OptionOne, OptionTwo } = useOptions<Person>(options);
I expect this as return type:
type ExpectedReturnType = {
ContactFields: {
fields: {
[key in keyof Person]?: {
label: string;
};
};
};
AddressFields: {
fields: {
[key in keyof Person]?: {
label: string;
};
};
};
};
Is something in typescript is possible in order to solve this ? or I may find an alternative ?
Thanks in advance for your answer :)
Solution 1:[1]
Consider this example:
// Option type that take for keys an OptionName and return fields based on Model
type Options<Model extends Record<string, any>, OptionName extends string = string> = {
[key in OptionName]: {
fields: Partial<Record<keyof Model, { label: string }>>
};
};
// Here a basic function who return options and expect getting return type based on options
function useOptions<Keys extends string, Model extends Record<string, any>,>(options: Options<Model, Keys>): Options<Model, Keys> {
return options as Options<Model, keyof typeof options>;
}
type Person = {
lastname: string;
firstname: string;
country: string;
};
const options = {
ContactFields: {
fields: {
lastname: {
label: 'Lastname',
},
firstname: {
label: 'Firstname',
},
},
},
};
const result = useOptions(options)
If you want to infer return type - you need to infer input type.
In order to infer input time you need to get rid of explicit types from const options and explicit generic parameter in useOptions<Person>.
If you are interested in inference on function arguments you can check my article
P.S. As you might have noticed I have added Keys generic argument to infer root keys of input parameter
Try to avoid using explicit generic parameters when you want to call your function. There are only two justified cases when you aare allowed to do it.
First When you have an empty array:
const handleArray = <T,>(arr: T[]) => arr
handleArray([]) // never []
handleArray<number>([]) // number[]
Second When you have an api call:
const handleApi = <T,>() => ({}) as T
handleApi<{ user: string }>() // {user: string}
Always try to infer your input parameters instead of explicitly declare them
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
