Conditional Types using the infer keyword allow you to extract types from complex data types. A classic example is extracting the element type from an array:
type ElementType<T> = T extends (infer U)[] ? U : T;
Here, infer U allows you to compute the type of the array element T. If T is an array, the type of its elements will be returned; otherwise, T itself will be returned.
Where they are used:
Example:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
Can you use multiple infer statements in a single conditional type? How does TypeScript interpret this case?
Wrong answer:
Correct answer:
type FirstArgument<T> = T extends (infer F, ...any[]) => any ? F : never; // But it's more correct with functions: type Args<T> = T extends (...args: infer A) => any ? A : never;
Story
A developer wrote a way to obtain the type of an object returned by a function but did not account for the possibility that the function might return a Promise. As a result, the return type was always Promise<any>, as no nested conditional with extract/infer was used. The code had to be refactored throughout the codebase.
Story
A generic type for unpacking nested arrays was introduced in the project, but the final else-branch was forgotten in the condition. TypeScript did not raise any error, but in some cases, the result was never, causing some utility typings in external libraries to break.
Story
A colleague attempted to extract property types from a large interface using a combine-conditional/infer type, but did not consider that some properties were themselves union types. As a result, unexpected combinations of union types were produced on output; the compiler passed them, but the logic worked incorrectly.