Q20 of 38 · TypeScript
What are conditional types in TypeScript, and what is the `infer` keyword?
Short answer
Short answer: Conditional types use `T extends U ? X : Y` to produce a different type based on a condition. The `infer` keyword, used inside the extends clause, lets TypeScript extract and name a type from a matched position — the mechanism behind `ReturnType`, `Parameters`, and `Awaited`.
Detail
Conditional types are a form of type-level if-else. They evaluate at compile time to produce one type or another.
Basic form: T extends U ? TrueType : FalseType. If T is assignable to U, the result is TrueType; otherwise FalseType.
Distributive behaviour: When T is a union type and the conditional type is applied, TypeScript distributes the condition over each union member: string | number extends string ? "yes" : "no" produces "yes" | "no".
infer keyword: Inside the extends clause, infer K declares a type variable that TypeScript fills in by matching the structure. This is how built-in types extract nested types:
type ReturnType<F> = F extends (...args: any[]) => infer R ? R : never— extracts the return typetype Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T— recursively unwraps
In test automation: Conditional types are used in advanced test helpers, fixture type utilities, and custom assertion type predicates. Understanding infer helps you read and debug complex type errors in Playwright's own type definitions.
// EXAMPLE
// Basic conditional
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// Distributive — over a union
type Flatten<T> = T extends Array<infer Item> ? Item : T;
type F1 = Flatten<string[]>; // string
type F2 = Flatten<number[]>; // number
type F3 = Flatten<string>; // string (not array, return as-is)
// infer — extract type from structure
type UnpackPromise<T> = T extends Promise<infer U> ? U : T;
type P = UnpackPromise<Promise<string>>; // string
type Q = UnpackPromise<number>; // number
// Practical: extract first param type
type FirstParam<F extends (...args: any[]) => any> =
F extends (first: infer P, ...rest: any[]) => any ? P : never;
function greet(name: string, age: number) {}
type Name = FirstParam<typeof greet>; // string