TypeScript
Beginner
1 min read
Conditional Types and Mapped Types Deep Dive
Example
// Distributive vs non-distributive conditional types
type IsArray<T> = T extends unknown[] ? true : false;
type DA = IsArray<string | number[]>; // boolean (distributive)
type IsArrayND<T> = [T] extends [unknown[]] ? true : false;
type NDA = IsArrayND<string | number[]>; // false (non-distributive, whole union)
// Deep mapped type with conditional branching
type Serialize<T> =
T extends Date ? string :
T extends (infer U)[] ? Serialize<U>[] :
T extends object ? { [K in keyof T]: Serialize<T[K]> } :
T;
interface Event {
id: number;
name: string;
date: Date;
tags: Date[];
meta: { createdAt: Date };
}
type SerializedEvent = Serialize<Event>;
// { id: number; name: string; date: string; tags: string[]; meta: { createdAt: string } }
// Flattening nested types
type Flatten<T> = T extends Array<infer U> ? Flatten<U> : T;
type F1 = Flatten<number[][][]>; // number
// Type-level function composition
type Compose<F, G> =
F extends (arg: infer A) => infer B
? G extends (arg: B) => infer C
? (arg: A) => C
: never : never;
type StrToNum = (s: string) => number;
type NumToBool = (n: number) => boolean;
type StrToBool = Compose<StrToNum, NumToBool>; // (arg: string) => boolean
Related Resources
TypeScript Reference
Complete tag & property list
TypeScript How-To Guides
Step-by-step practical guides
TypeScript Exercises
Practice what you've learned
More in TypeScript