Issue
I would like to learn the algorithm by which Typescript attempts to infer types in signatures using conditional types.
Example 1: here we correctly infer T as number:
type Args<T> = { value: T }
function foo<T>(args: Args<T>) { return args; }
// correctly inferred as foo<number>
foo({ value: 123 });
Example 2: conditional types and inferring unknown
type StringOrNever<T> = T extends string ? string : never;
type Args<T> = { value: StringOrNever<T>; }
function foo<T>(args: Args<T>) { return args; }
// both calls inferred as foo<unknown> :(
foo({ value: 123 });
foo({ value: "some string" });
Example 3: correct (or "expected") inference but weird types
type StringOrNever<T> = T extends string ? string : never;
type Args<T> = { value: StringOrNever<T> & T; }
function foo<T>(args: Args<T>) { return args; }
// inferred as foo<number>
foo({ value: 123 });
// inferred as foo<"Typescript">
foo({ value: "Typescript" });
What I would like to learn is:
- Why does Typescript infer
unknownforTin example 2, and what exactly causes it to infer types forTas I expect in example (3) - If there is some general algorithm which Typescript follows to try to infer a generic type argument. Like for example trying a sequence of candidates, in which first candidate is always a type of argument and the last candidate is
unknown(kinda like a last resort).
Solution
A generic type inference is a complicated and sometimes hard to understand concept in TypeScript. But the generic type must be used in a position where it can be substituted by a type of the given argument.
So for example 1
function foo<T>(args: { value: T }) { return args; }
if you pass a value of type { value: string }, we can substitute T with string.
The same can not be said about example 2.
type StringOrNever<T> = T extends string ? string : never;
function foo<T>(args: { value: StringOrNever<T> }) { return args; }
T is only used in the comparison part of the condtional. On the right side of the conditional we only have string and never. T is not in any place which could be substituted by the given type, so unknown is inferred as the default type.
If you want inference to succed, you will need to put T on the right side of the conditional.
type StringOrNever<T> = T extends string ? T : never;
function foo<T>(args: { value: StringOrNever<T> }) { return args; }
This allows TypeScript to infer T based on the given type of value.
Answered By - Tobias S.
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.