Suggest an editImprove this articleRefine the answer for “Conditional generics”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)The construct `T extends U ? X : Y` is called a **conditional type** and works similarly to the `?:` operator in JavaScript, but at the type level: if type `T` is assignable to type `U`, the result is type `X`, otherwise it is type `Y`. **Key point:** when `T` is a union type, the conditional type is applied to each union member separately - this is called distributive behavior.Shown above the full answer for quick recall.Answer (EN)ImageA construct of the form `T extends U ? X : Y` in TypeScript is called a **conditional type**. It works **similarly to the** `?:` **operator in JavaScript**, but it is applied **at the type level**, not to values. --- ## General form ```javascript T extends U ? X : Y ``` This can be read as: > "If type `T` is **assignable** to type `U`, > the result is type `X`, > otherwise, type `Y`." --- ## Example 1: Basic example ```javascript type IsString<T> = T extends string ? "yes" : "no"; type A = IsString<string>; // "yes" type B = IsString<number>; // "no" ``` > If `T` is assignable to `string`, `"yes"` is returned, otherwise `"no"`. --- ## Example 2: Checking a subtype ```javascript type ExtendsExample<T> = T extends number ? number : string; let a: ExtendsExample<42>; // number let b: ExtendsExample<"foo">; // string ``` Here TypeScript checks whether `T` is a **subtype** of `number`; if so, it returns `number`. --- ## Example 3: A complex type with objects ```javascript type HasId<T> = T extends { id: any } ? "Has ID" : "No ID"; type A = HasId<{ id: number; name: string }>; // "Has ID" type B = HasId<{ name: string }>; // "No ID" ``` > Works even with object structures (structural type checking). --- ## Example 4: Extracting a type with `infer` Conditional types are often combined with `infer` to **extract** nested types: ```javascript type UnwrapPromise<T> = T extends Promise<infer U> ? U : T; type A = UnwrapPromise<Promise<string>>; // string type B = UnwrapPromise<number>; // number ``` > "If `T` is `Promise<X>`, return `X`, otherwise return `T` itself." --- ## Example 5: Filtering types ```javascript type ExcludeString<T> = T extends string ? never : T; type Result = ExcludeString<string | number | boolean>; // number | boolean ``` Here TypeScript applies the condition **to each member of the union type** (`string`, `number`, `boolean`), then combines the results, which is called **distributive behavior** of conditional types. --- ## Example 6: The reverse case: extracting only strings ```javascript type ExtractString<T> = T extends string ? T : never; type A = ExtractString<string | number | boolean>; // string ``` --- ## Example 7: Usage with `keyof` ```javascript type PropertyExists<T, K> = K extends keyof T ? true : false; type Obj = { id: number; name: string }; type A = PropertyExists<Obj, "id">; // true type B = PropertyExists<Obj, "age">; // false ``` > We check whether a specific key `K` exists on object `T`. --- ## Example 8: Comparison with "if" at the value level JS: ```javascript const result = condition ? value1 : value2; ``` TS type: ```javascript type Result<T> = T extends true ? "yes" : "no"; ``` > Works **at compile time**, not while the code runs. --- ## How this works internally - `T extends U` is a **type compatibility check** (not inheritance). - If `T` is a union (for example `A | B | C`), the conditional type is applied **to each member of the union** separately: ```javascript (A | B) extends U ? X : Y -> (A extends U ? X : Y) | (B extends U ? X : Y) ``` This is called the **distributive behavior of conditional types**. --- ## Example 9: The non-distributive variant To turn off "distribution" over a union type, `T` is wrapped in a tuple `[...]`: ```javascript type NonDistributive<T> = [T] extends [string] ? "yes" : "no"; type A = NonDistributive<string | number>; // "no" ``` > Without the brackets, the result would be `"yes" | "no"`. --- ## Example 10: Implementing built-in utilities with conditional types | Utility | Implementation | Description | |---|---|---| | `Exclude<T, U>` | `T extends U ? never : T` | Removes from T every type assignable to U | | `Extract<T, U>` | `T extends U ? T : never` | Keeps only the types from T that are assignable to U | | `NonNullable<T>` | `T extends null | undefined ? never : T` | | `ReturnType<T>` | `T extends (...args: any[]) => infer R ? R : any` | Extracts the return type of a function | --- ## Summary | Element | Meaning | |---|---| | **Syntax** | `T extends U ? X : Y` | | **Meaning** | "If `T` is a subtype of `U`, then `X`, otherwise `Y`" | | **Applies to** | types, not values | | **Feature** | works "distributively" with union types | | **Often used with** | `infer`, `Extract`, `Exclude`, `NonNullable`, `ReturnType` |For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.