TypeScript Conditional Types and infer: T extends U ? X : Y
November 6, 2026
Welcome to episode 42 of TypeScript from Zero, a series for complete beginners (no JavaScript needed). Welcome back to TypeScript from zero! A conditional type is an if statement for types. Given a type, it picks one of two answers. Combined with the word infer, it can even take types apart. A new file called conditional.ts in the tasks project. Every code block below is the exact code and output from the video, ready to copy.
- All episodes are in the TypeScript from Zero playlist.
- Versions used: Node.js 26.13.0, npm 11.20.0, TypeScript 7.0.2.
Setup
We start in the terminal and open the project in VS Code:
cd tasks
touch src/conditional.ts
code .1. If for types
It looks like the ternary operator from episode six. T extends string, question mark, yes, colon, no. If T fits into string, the answer is the text type yes, else no. For string it is yes, so a is fine. For number it is no, so giving yes is an error: type yes is not assignable to type no. TS2322. The error tells us what the answer was.
src/conditional.ts:
type IsString<T> = T extends string ? "yes" : "no";
const a: IsString<string> = "yes";
const b: IsString<number> = "yes";$ npx tsc --noEmit
src/conditional.ts:3:7 - error TS2322: Type '"yes"' is not assignable to type '"no"'.
3 const b: IsString<number> = "yes";
~
Found 1 error in src/conditional.ts:32. Fix
We write no, and tsc is quiet.
src/conditional.ts:
type IsString<T> = T extends string ? "yes" : "no";
const a: IsString<string> = "yes";
const b: IsString<number> = "no";$ npx tsc --noEmit3. infer
Infer declares a new type variable inside the condition. Element of takes T, and asks: is it a list of something? Call that something U. If yes, the answer is U. So element of a list of numbers is number, and the five is fine. For a list of strings, the number one is an error, and the message says: not assignable to type string. That is U, found by tsc.
src/conditional.ts:
type IsString<T> = T extends string ? "yes" : "no";
const a: IsString<string> = "yes";
const b: IsString<number> = "no";
type ElementOf<T> = T extends (infer U)[] ? U : never;
const e: ElementOf<number[]> = 5;
const bad: ElementOf<string[]> = 1;$ npx tsc --noEmit
src/conditional.ts:6:7 - error TS2322: Type 'number' is not assignable to type 'string'.
6 const bad: ElementOf<string[]> = 1;
~~~
Found 1 error in src/conditional.ts:64. Fix
We use a string, and it works.
src/conditional.ts:
type IsString<T> = T extends string ? "yes" : "no";
const a: IsString<string> = "yes";
const b: IsString<number> = "no";
type ElementOf<T> = T extends (infer U)[] ? U : never;
const e: ElementOf<number[]> = 5;
const bad: ElementOf<string[]> = "one";$ npx tsc --noEmit5. Unwrap
The same trick takes a value out of a promise. Unwrap asks: is T a promise of R? Then the answer is R. Otherwise, the answer is T itself. Promise of string unwraps to string, and a plain number stays a number. This is how awaited from episode thirty two is built.
src/conditional.ts:
type IsString<T> = T extends string ? "yes" : "no";
const a: IsString<string> = "yes";
const b: IsString<number> = "no";
type ElementOf<T> = T extends (infer U)[] ? U : never;
const e: ElementOf<number[]> = 5;
const bad: ElementOf<string[]> = "one";
type Unwrap<T> = T extends Promise<infer R> ? R : T;
const u: Unwrap<Promise<string>> = "ok";
const v: Unwrap<number> = 7;
console.log(a, b, e, bad, u, v);$ node src/conditional.ts
yes no 5 one ok 76. Unions
One surprise. When T is a union, a conditional type is applied to every member separately. To array of string or number is not a list of mixed things, it is a list of strings, or a list of numbers. We see it in the error: an item must be a string or a number, and a boolean is neither. That is called a distributive conditional type, and mostly it is what we want. We delete the line, and tsc is quiet.
src/conditional.ts:
type IsString<T> = T extends string ? "yes" : "no";
const a: IsString<string> = "yes";
const b: IsString<number> = "no";
type ElementOf<T> = T extends (infer U)[] ? U : never;
const e: ElementOf<number[]> = 5;
const bad: ElementOf<string[]> = "one";
type Unwrap<T> = T extends Promise<infer R> ? R : T;
const u: Unwrap<Promise<string>> = "ok";
const v: Unwrap<number> = 7;
console.log(a, b, e, bad, u, v);
type ToArray<T> = T extends unknown ? T[] : never;
const x: ToArray<string | number> = [true];$ npx tsc --noEmit
src/conditional.ts:12:38 - error TS2322: Type 'boolean' is not assignable to type 'string | number'.
12 const x: ToArray<string | number> = [true];
~~~~
Found 1 error in src/conditional.ts:12Recap
Today: conditional types as an if for types, infer to capture a part of a type, unwrapping a promise, and the distribution over unions. Next episode: template literal types.
7. `string
numberis split:string[]
$ npx tsc --noEmit
src/conditional.ts:12:38 - error TS2322: Type 'boolean' is not assignable to type 'string | number'.
12 const x: ToArray<string | number> = [true];
~~~~
Found 1 error in src/conditional.ts:12