TypeScript Tutorial · Generics and Type Manipulation · Lesson 19 of 25
TypeScript Mapped and Conditional Types
Transform every property with mapped types, and choose types with conditions.
Mapped types
A mapped type loops over keys to build a new type. This is how Partial and Readonly are implemented:
type MyReadonly<T> = { readonly [K in keyof T]: T[K] };
type Nullable<T> = { [K in keyof T]: T[K] | null };
type Getters<T> = { [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K] };
interface Point { x: number; y: number }
const p: MyReadonly<Point> = { x: 1, y: 2 };
const blank: Nullable<Point> = { x: null, y: 5 };
const getters: Getters<Point> = { getX: () => p.x, getY: () => p.y };
console.log(p, blank, getters.getX() + getters.getY());
Output
{ x: 1, y: 2 } { x: null, y: 5 } 3Conditional types
T extends U ? X : Y picks a type based on a condition. With infer you can extract parts of a type:
type IsString<T> = T extends string ? "yes" : "no";
type ElementOf<T> = T extends (infer E)[] ? E : never;
type Unwrap<T> = T extends Promise<infer V> ? V : T;
const a: IsString<"hi"> = "yes";
const b: IsString<42> = "no";
const c: ElementOf<boolean[]> = true;
const d: Unwrap<Promise<number>> = 42;
console.log(a, b, c, d);
Output
yes no true 42
Template literal types
Build string types from other string types:
type Method = "get" | "post";
type Resource = "user" | "order";
type Handler = `${Method}${Capitalize<Resource>}`; // "getUser" | "getOrder" | "postUser" | "postOrder"
type CssSize = `${number}px` | `${number}rem`;
const handlers: Record<Handler, () => string> = {
getUser: () => "GET /user",
getOrder: () => "GET /order",
postUser: () => "POST /user",
postOrder: () => "POST /order",
};
const gap: CssSize = "1.5rem";
console.log(Object.keys(handlers).length, handlers.postOrder(), gap);
Output
4 POST /order 1.5rem
Test yourself
What does infer do inside a conditional type?
Infers a variable's valueCaptures part of a type into a new type variableDisables type checkingCreates a runtime check
Show answer
Answer: Captures part of a type into a new type variable