Conditional types choose between types, and mapped types produce an object type from a list of keys. Together with type operators they let a type be computed from another type. Everything on this page happens in the compiler alone.
Conditional types
ConditionalType:
CheckType extends ExtendsType ? TrueType : FalseType
A conditional type (2.8) gives TrueType if CheckType is
assignable to ExtendsType, and FalseType otherwise. In the true
branch the check type is known to satisfy the condition
(Handbook:
Conditional Types).
type IsString<T> = T extends string ? "yes" : "no";
type A = IsString<"x">; // "yes"
type B = IsString<1>; // "no"
type MessageOf<T> = T extends { message: unknown } ? T["message"] : never;
type M = MessageOf<{ message: string }>; // string
If the check type is still a generic type parameter, the conditional type is
deferred until the parameter is known. A deferred conditional type is assignable
to the union of its branches, but a concrete value is not assignable to it, so a generic
function that returns one needs an assertion. If the check type is any, the
result is the union of both branches.
A conditional type may refer to itself (4.1). Recursion in the tail position does not count towards the depth limit (4.5), so it can run much further.
type Repeat<S extends string, N extends number, A extends string[] = []> =
A["length"] extends N ? A : Repeat<S, N, [...A, S]>;
type R = Repeat<"x", 3>; // ["x", "x", "x"]
infer
infer declares a new type variable inside the extends part of
a conditional type (2.8). The match fills it in, and it can be used in the true
branch. Since 4.7 it can have its own constraint.
type Flatten<T> = T extends Array<infer I> ? I : T;
type F1 = Flatten<string[]>; // string
type F2 = Flatten<number>; // number
type First<T> = T extends [infer S extends string, ...unknown[]] ? S : never;
type S1 = First<["a", 1]>; // "a"
type S2 = First<[1, 1]>; // never
If the same infer name appears several times, candidates in covariant
positions combine as a union and those in contravariant positions as an intersection. This
gives the well-known way of turning a union into an intersection.
type CoVar<T> = T extends { a: infer X; b: infer X } ? X : never;
type U = CoVar<{ a: string; b: number }>; // string | number
type ContraVar<T> = T extends { a: (x: infer X) => void; b: (x: infer X) => void } ? X : never;
type I = ContraVar<{ a: (x: string) => void; b: (x: number) => void }>; // never
type UnionToIntersection<U> =
(U extends any ? (x: U) => void : never) extends (x: infer I) => void ? I : never;
type J = UnionToIntersection<{ a: 1 } | { b: 2 }>; // { a: 1 } & { b: 2 }
When a function has several overloads, inference uses only the last signature.
Distribution
A conditional type distributes over a union when the checked type is a bare type parameter. The condition is applied to each member, and the results are joined.
type ToArray<T> = T extends any ? T[] : never;
type A = ToArray<string | number>; // string[] | number[]
type B = ToArray<boolean>; // false[] | true[]
type C = ToArray<never>; // never: a union with no members
type NoDistribute<T> = [T] extends [any] ? T[] : never;
type D = NoDistribute<string | number>; // (string | number)[]
type E = NoDistribute<never>; // never[]
type IsNever<T> = [T] extends [never] ? true : false;
Distribution is how Exclude and Extract work, and how a type
can test for never safely.
Mapped types
MappedType:
{ [ [+|-] readonly ] [ Name in KeyType [as NameType] ] [ [+|-] ? ] : Type [;] }
A mapped type (2.1) goes through the members of a key type and produces one property for each. It may hold nothing else, so a mapped type with an additional property is an error (Handbook: Mapped Types).
type Flags<T> = { [K in keyof T]: boolean };
type F = Flags<{ a: number; b: string }>; // { a: boolean; b: boolean }
type Fixed = { [K in "x" | "y"]: number }; // { x: number; y: number }
keyof another typeModifiers
A mapped type written over keyof T keeps each property’s
readonly and ?; it is called homomorphic. Since 2.8,
+ and - add or remove a modifier.
| Form | Effect |
|---|---|
[K in keyof T] | Modifiers kept |
readonly [K in …] or +readonly | Added |
-readonly [K in …] | Removed |
[K in …]? or +? | Optional added |
[K in …]-? | Optional removed |
type Mutable<T> = { -readonly [K in keyof T]: T[K] };
type Concrete<T> = { [K in keyof T]-?: T[K] };
-readonly and -? remove modifiersKey remapping
An as clause (4.1) renames each key. A name that resolves to
never drops the property. The key type can be any union, not just
strings.
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type G = Getters<{ name: string }>; // { getName: () => string }
type WithoutKind<T> = { [K in keyof T as Exclude<K, "kind">]: T[K] };
type Handlers<E extends { kind: string }> = {
[X in E as X["kind"]]: (event: X) => void;
};
as clause can filter keys or map any union to namesArrays and tuples
A homomorphic mapped type applied to an array or tuple (3.1) maps the elements and keeps the array or tuple shape. Applied to a primitive, it gives back the primitive.
type Promised<T> = { [K in keyof T]: Promise<T[K]> };
type A = Promised<[string, number]>; // [Promise<string>, Promise<number>]
type B = Promised<string[]>; // Promise<string>[]
type C = Promised<string>; // string
Utility types
The standard library declares these types globally. Most are plain mapped or conditional
types, written in lib.es5.d.ts
(Handbook:
Utility Types).
| Type | Result | Since |
|---|---|---|
Partial<T> | All properties optional | 2.1 |
Required<T> | All properties required | 2.8 |
Readonly<T> | All properties read-only | 2.1 |
Record<K, T> | An object with keys K and values T | 2.1 |
Pick<T, K> | Only the properties in K | 2.1 |
Omit<T, K> | All but the properties in K | 3.5 |
Exclude<T, U> | Union members of T not assignable to U | 2.8 |
Extract<T, U> | Union members of T assignable to U | 2.8 |
NonNullable<T> | T without null and undefined | 2.8 |
Parameters<F> | A function’s parameters as a tuple | 3.1 |
ConstructorParameters<C> | A constructor’s parameters as a tuple | 3.1 |
ReturnType<F> | A function’s return type | 2.8 |
InstanceType<C> | The instance type of a constructor | 2.8 |
Awaited<T> | The type a Promise resolves to, recursively | 4.5 |
NoInfer<T> | T, but not a source of inference | 5.4 |
ThisParameterType<F> | The type of a function’s this parameter, or unknown | 3.3 |
OmitThisParameter<F> | The function type without its this parameter | 3.3 |
ThisType<T> | A marker: sets the type of this in an object literal | 2.3 |
Uppercase<S>, Lowercase<S>, Capitalize<S>, Uncapitalize<S> | String case, implemented in the compiler | 4.1 |
The definitions show the same techniques as above:
type Partial<T> = { [P in keyof T]?: T[P] };
type Pick<T, K extends keyof T> = { [P in K]: T[P] };
type Exclude<T, U> = T extends U ? never : T;
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type NonNullable<T> = T & {};
type ReturnType<T extends (...args: any) => any> =
T extends (...args: any) => infer R ? R : any;
lib.es5.d.ts