Conditional Types
- Basic conditional types
just like
if elsestatement. - Nested conditional types
just like
switch casestatement. - Distributive conditional types
just like
mapstatement (loopstatement) onuniontype. - Conditional types make
TypeScriptbecome real programming type system:TypeScripttype system is Turing Complete.
Basic Conditional Types
interface Animal {
live: () => void
}
interface Dog extends Animal {
woof: () => void
}
type Example1 = Dog extends Animal ? number : string
// => type Example1 = number
type Example2 = RegExp extends Animal ? number : string
// => type Example2 = string
Nested Conditional Types
- Conditional types can be nested.
- 通过嵌套条件类型, 可以将类型约束收拢到精确范围.
type TypeName<T> = T extends string
? 'string'
: T extends number
? 'number'
: T extends boolean
? 'boolean'
: T extends undefined
? 'undefined'
: T extends Function
? 'function'
: 'object'
Index Conditional Types
Conditional types are able to access members of provided types:
interface QueryOptions {
throwIfNotFound: boolean
}
type QueryResult<Options extends QueryOptions>
= Options['throwIfNotFound'] extends true ? string : string | undefined
declare function retrieve<Options extends QueryOptions>(
key: string,
options?: Options
): Promise<QueryResult<Options>>
// Returned type: string | undefined
await retrieve('1')
// Returned type: string | undefined
await retrieve('2', { throwIfNotFound: Math.random() > 0.5 })
// Returned type: string
await retrieve('3', { throwIfNotFound: true })
Mapped Conditional Types
type MakeAllMembersFunctions<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => any ? T[K] : () => T[K]
}
type MemberFunctions = MakeAllMembersFunctions<{
alreadyFunction: () => string
notYetFunction: number
}>
// Type:
// {
// alreadyFunction: () => string,
// notYetFunction: () => number,
// }
Distributive Conditional Types
Type distributivity:
- Conditional types in which checked type is
naked type parameterare called DCT. - DCT are automatically distributed over union types during instantiation.
- When conditional types act on a generic type, they become distributive when given a union type.
( A | B | C ) extends T ? X : Y相当于(A extends T ? X : Y) | (B extends T ? X : Y) | (B extends T ? X : Y).- 没有被额外包装的联合类型参数, 在条件类型进行判定时会将联合类型分发, 分别进行判断.
// "string" | "function"
type T1 = TypeName<string | (() => void)>
// "string" | "object"
type T2 = TypeName<string | string[]>
// "object"
type T3 = TypeName<string[] | number[]>
type Naked<T> = T extends boolean ? 'Y' : 'N'
type Wrapped<T> = [T] extends [boolean] ? 'Y' : 'N'
/*
* 先分发到 Naked<number> | Naked<boolean>
* 结果是 "N" | "Y"
*/
type Distributed = Naked<number | boolean>
/*
* 不会分发 直接是 [number | boolean] extends [boolean]
* 结果是 "N"
*/
type NotDistributed = Wrapped<number | boolean>