type MyReturnType<F> = F extends (...args: any[]) => infer R ? R : never;
type UnwrapPromise<T> = T extends Promise<infer V> ? V : T;
type A = MyReturnType<() => number>; // number
type B = UnwrapPromise<Promise<string>>; // string
type C = UnwrapPromise<boolean>; // boolean
A conditional type is an if/else for types, with the form T extends U ? X : Y. infer captures part of a type by pattern matching, inside that extends check.
A conditional type works like a ternary at the type level. If T can be assigned to U, the result is X. Otherwise it is Y. It is mostly useful inside generics, where T is not known yet.
infer can only appear in the extends clause. It declares a new type variable, and TypeScript fills it in by matching the pattern. Think of it as "match this shape and give a name to the part I want".
- In
MyReturnType, the pattern is any function.infer Rcaptures whatever its return type is. - In
UnwrapPromise,infer Vcaptures the value inside aPromise. A type that is not a promise does not match, so the result isTitself.
The built-in Awaited goes further. It unwraps nested promises again and again, so Promise<Promise<string>> becomes string.
T extends U ? X : Yis an if/else (ternary) for types.inferdeclares a type variable that TypeScript fills in by pattern matching.inferis only allowed in theextendsclause of a conditional type.- Built-in types like
ReturnTypeandParametersare written this way.
Read more