Reputation: 47
I'm trying to make TypeScript automagically infer the type of variable by it's property value;
Let's assume we have the following function
function ensureReturnTypeIsRight(...args: any[]): ReturnType {
return Math.random() > 0.5 ? {
title: 'prop2',
params: ['1', '2', '3'],
} : {
title: 'prop1',
params: undefined,
};
}
And
type SomeMap = {
'prop1': string,
'prop2': (...args: string[]) => string,
}
type ReturnType = `
Type that supposed to throw an error if title is not a key of SomeMap,
to throw an error in case title is 'prop2' but no params were provided
to throw an error in case title is 'prop1' but params were provided
but not to throw an error in case everything is correct.
When we try to assign a value to a variable of that type (in our case 'variable' is return value)
`
It can be done with generic type like this
type ArgumentTypes<F extends Function> = F extends (...args: infer A) => any ? A : never;
type GenericReturnType<K keyof SomeMap = any> = {
title: K,
params: SomeMap[K] extends Function ? ArgumentTypes<SomeMap[K]> : undefined,
};
And function written like
function ensureReturnTypeIsRight(...args: any[]): GenericReturnType {
const shouldHaveParams = Math.random() > 0.5;
if (shouldHaveParams) {
const value: GenericReturnType<'prop2'> = {
title: 'prop2',
params: ['1', '2', '3'],
};
return value;
}
const value: GenericReturnType<'prop1'> = {
title: 'prop1',
params: undefined,
};
return value;
}
So TypeScript will check if it's possible to assign a value of following type to a variable. But is it possible to do so without generics?
I'm sorry for any unclear thoughts.
Upvotes: 1
Views: 247
Reputation: 249536
What you are really looking for is a union taht should look something like:
type FnReturnType = {
title: "prop1";
params: undefined;
} | {
title: "prop2";
params: string[];
}
Such a union would ensure the return type is correct for a given title
. Fortunately we can generate such a union from SomeMap
using some mapped types.
type FnReturnType= {
[K in keyof SomeMap] -?: { // Transform all properties of SomeMap to a type that is a member of our final union
title: K,
params: SomeMap[K] extends (...args: any[]) => any ? Parameters<SomeMap[K]> : undefined,
}
}[keyof SomeMap]; // get a union of all types in our previously generated type
function ensureReturnTypeIsRight(...args: any[]): FnReturnType {
const shouldHaveParams = Math.random() > 0.5;
if (shouldHaveParams) {
return {
title: 'prop2',
params: ['1', '2', '3'],
};
}
return {
title: 'prop1',
params: undefined,
};
}
Upvotes: 2