Conditional types and infer in TypeScript
A type-level if, and the keyword that pulls a type out of a position.
type Unwrap<T> = T extends Promise<infer Inner> ? Inner : T;
type ElementOf<T> = T extends readonly (infer Item)[] ? Item : never;
type FirstArg<F> = F extends (first: infer A, ...rest: never[]) => unknown
? A
: never;
type A = Unwrap<Promise<string>>;
type B = ElementOf<number[]>;
type C = FirstArg<(name: string, age: number) => void>;
type Flatten<T> = T extends readonly (infer Item)[] ? Flatten<Item> : T;
type Deep = Flatten<number[][][]>;
const a: A = "unwrapped";
const b: B = 42;
const c: C = "first";
const deep: Deep = 1;
console.log(a, b, c, deep);
How it works
A extends B ? X : Ychooses at the type level.infernames a type in the position it appears.- That is how ReturnType and Awaited are written.
Keywords and builtins used here
ABCDeepPromiseTconstextendsinfernevernumberreadonlystringtype
The run, in numbers
- Lines
- 18
- Characters to type
- 564
- Tokens
- 181
- Three-star pace
- 105 tpm
At the three-star pace of 105 tokens a minute, this run takes about 103 seconds.
Step 2 of 3 in Conditional types, step 2 of 12 in Type-level programming.