条件类型
在最有用的程序的核心,我们必须根据输入做出决定。JavaScript 程序也不例外,但考虑到值很容易内省,这些决定也基于输入的类型。条件类型有助于描述输入和输出类型之间的关系。
interface Animal {
live(): void;
}
interface Dog extends Animal {
woof(): void;
}
type Example1 = Dog extends Animal ? number : string;
type Example2 = RegExp extends Animal ? number : string;
条件类型的形式看起来有点像 JavaScript 中的条件表达式 (condition ? trueExpression : falseExpression
):
type SomeType = any;
type OtherType = any;
type TrueType = any;
type FalseType = any;
type Stuff =
SomeType extends OtherType ? TrueType : FalseType;
当 extends
左侧的类型可以分配给右侧的类型时,您将获得第一个分支("true" 分支)中的类型;否则,您将在后一个分支("false" 分支)中获得类型。
从上面的例子中,条件类型可能不会立即有用——我们可以告诉自己是 Dog extends Animal
还是选择 number
或 string
!但是条件类型的强大之处在于将它们与泛型一起使用。
例如,让我们以下面的 createLabel
函数为例:
interface IdLabel {
id: number /* some fields */;
}
interface NameLabel {
name: string /* other fields */;
}
function createLabel(id: number): IdLabel;
function createLabel(name: string): NameLabel;
function createLabel(nameOrId: string | number): IdLabel | NameLabel;
function createLabel(nameOrId: string | number): IdLabel | NameLabel {
throw "unimplemented";
}
IsKEfnpLn1inAplmkT9S3kkElZm5o7BNmKI9NqAAwJoUjeSKm3EZr1sMpug0+dvybHIHeMLSibno1gGF4VeAs6VTFLBmNxAaBqz0Usx9CWHs/JGFxZhEGZjd8Yf7/JEPL/L8ztAfMd5c2SJaZUoNUR2XxACqJvJkFowXYmWwH5A=
- 如果一个库必须在其 API 中一遍又一遍地做出相同的选择,这将变得很麻烦。
- 我们必须创建三个重载:一个用于确定类型的每种情况(一个用于
string
,一个用于number
),一个用于最一般的情况(采用string | number
)。对于createLabel
可以处理的每个新类型,重载的数量呈指数增长。
2XMeyBRNdt0kZMdT+CewC0yHrorVJx0pB1124UwTm9PWT2hyAoPgo2BwCOY+SAJj8sK219olcRSlHAXjX0mBuA==
interface IdLabel {
id: number /* some fields */;
}
interface NameLabel {
name: string /* other fields */;
}
type NameOrId<T extends number | string> = T extends number
? IdLabel
: NameLabel;
s8fOC2DlyODAaMcDuFhwnGkJk/bpiyOggLLvvW4Zk0/uG1QzKQyRaytRTCGKKEio3BFj3lOgVHBvH2APpt4BYsNcFt47X1j518sk+kSFWyviX7lRIlSoJp2BIlnc//cD
interface IdLabel {
id: number /* some fields */;
}
interface NameLabel {
name: string /* other fields */;
}
type NameOrId<T extends number | string> = T extends number
? IdLabel
: NameLabel;
function createLabel<T extends number | string>(idOrName: T): NameOrId<T> {
throw "unimplemented";
}
let a = createLabel("typescript");
let b = createLabel(2.8);
let c = createLabel(Math.random() ? "hello" : 42);
条件类型约束
qpMOX1VChFfEAvR8AVsNGrUPtYHfAzceo0FZh7I37ga3kL1u9g5PMQ7QXu8S0NUOelPsufStBAlJ9lrXCivUfGzXZPH1Qimv7tnrabjtF+Cmk5JgvBEroFWAECaJvIwh7lR+2wQwExgh3RmdLssX2ayqD4IeWzrwyL2c4HWPn8k/IjwOz6OBv8Ne8cBcafg4dx3GLlDTsxMHhmrdXBUFlATNjiVbq8UO+7By0/fGvVEwLnNmFmz0DW5NKU8M4xp95ppI5p+3wgebbEIW5XDEhfQy3ECyZGnt5BMyA7Cta+8=
eQYKJ1QpeskDhfY2KH7N8mbtbJ1NMNj1p5jOf9399cCn5QoxwQOvRiHLIX9/EB2w
type MessageOf<T> = T["message"];
5QWVDuHIV2CjtPnzd5EYLks9Vh/dxXbIgBIXz01xt1ANgZ8tYXMl1gKeNUrh4o+v1eZ+l8zFW05ym2cDhkwPQzjTQIqxggd/MAS2tPEC0X4HIYkBp2k0Q23wY5ILnn3coAW8ADp9rCLhW3ZbOno5EG7AtWrz4NRvNEfcnW03T1A47veE6LUvIGZh1dosSNrao0Bh/obMDKR2qj0cbpMMpZ7UrHJ4l0JQJdlfuQd+42xgQ0Jh0RbQA+GvqWjK9wYw
type MessageOf<T extends { message: unknown }> = T["message"];
interface Email {
message: string;
}
type EmailMessageContents = MessageOf<Email>;
LRTMURNNJ/ZsxZMvHeaeygy9ynBYneUfOPri/0RTjQ9AACtxc98mqcC00jZjFdcJ4R2agLpuw5BXHXN+OLrCQWtxH7DvvWLpyp9EU2uV2Nttq0yNGUo71W4rbjKO1ZPCYvWhvEkxa4NSEohnQ4WtNtb/ffvwexr9iqHpCNiQ1agFLd06D6y9FcbkFC517JqiDqr4TIKFGLMZCMmgrr8LhLkj7td89o5h3N5HxRaBeQpiDiiysmRwmTpgKX3spWxPtDef5dWQebQJqWOA+8eZl8CxnAGaQGeKrhViI/h5M/IYfCFi3Uhp0Z59i2BH2EzpkpuV3GAAn6ezdvKbGTEjwgnjFYDfnfG/LKU7beZI3BY=
type MessageOf<T> = T extends { message: unknown } ? T["message"] : never;
interface Email {
message: string;
}
interface Dog {
bark(): void;
}
type EmailMessageContents = MessageOf<Email>;
type DogMessageContents = MessageOf<Dog>;
5WT106esYyuSvSlxRK9aNWXnCnb+9PQXKM/4U+IU98viIBTTB6FIGOXAa/TH1BdLJwd5a1BFjEgyDxF5AsNBMyeuj6oMWp4J0QZiYh7NvOKPIF3v7/Fe9yFDHopSgwMZ
rRU1RLdJLB7A3BKx9uvX4W2d0GavYwP60SrFnt7LeOIICT8Wm/J0aJB9wJSvlf9DtDsZWf9QuKFV8RSINv2lrePuH05MnEfOehtpQPGox1KbIXDPExbcZn7mu2Jpr8aMg4UpUBTPTHhpByczkXSNwuYXw4an5oZU7B0DMDOpBnmQWN7xzMQAi8b3gq96P9uiCyDS+nsr3bgA0d2UuabEiA==
type Flatten<T> = T extends any[] ? T[number] : T;
// Extracts out the element type.
type Str = Flatten<string[]>;
// Leaves the type alone.
type Num = Flatten<number>;
LWTACxKivgNdesPxvQt+zFdwAi1EHZ7bqGTLp/MXVvdBKO4TYvDl17VV6O11QavpynsttC+6IIEob/fpfFrn+cGzQLrtAJP7QR3fEHwAkahJtr8oO46B3LZPoXuESu5sUFl/l9CuFOhjkchvlkoLeVDFcN+wWrq+B7C/0ks0G+fyVsdVCBNXn9XUyathHkXANKUAq2BZ8uNuLE5/UjkkJSn0B1fpzWjKI4gIgbRaazJAilwu6eHMhhu6hXfwSTp3
在条件类型中推断
m9kHQ/oIbwjTYgLWT6o/b9lNuwnTvRBNblWyxEtEDK9AEF2gIn/SbAjOe2R47ynqUl7TnB9GMlTggBiZEz6mx9Ud/zUwZCS7t8j3SlhpChHSCgly0RLgykWcafQ58mV5RFdZeJVVCeF1cC2QVVpNMaFunj4u6I0/B84NAES62X+xvvkOagKCl7G+4OCSO2J//J6WFq93/63z937+gGQFzg==
JJ4GAa3i9TM9QtwkzQyjc4Zk2NB6xJGk6iKMGPjGr4c3zRswqcBg7Wf8dPD7ef8BxCyYpyay5F3h3Wm5LxvViKh5Qn9fuyKPrpJqwkKQNFF+AD2TpQ5M2T9xAf1FJu1gqg1vAU3kzfFswUY4BkrojmrjnWNwPBFA/eVhSOo7AA3ff9EOH7rB/ZyPl/S2N+Wee/WIUNCkDxfpK9YTT7HI8S6/DcK9KMGmZUazlsRxEubpngxTBijpJ4baxxeuM92snNMQlinNIKIJpNVYwQohy4DGRT1EJP37N0Fsf1cXqPsecINMqUYKDqmX17NT7s+DM70uOii7CVNMYQAWcAFVAIVBU2eYRC5J0kLuoUkagMfzB2Ewh+1UOPJ8VKs0L7K6
type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;
FtRK/7p9um4giWuKdk8HBy5+JspACxUl82iQjN4xfuq6nYrEDzY96WLBAwx1vv7pe8+Z+VyqhAC7IbbJ2kVQCdZpVWppwIz3Ia75h13yI4EUYqUnQWkkiIGKIIQOvC/GuH4GTdwW7fvZu3vr2HEJkbpr36K/OArkrdllwS077cvpauaYvDzmmQbjb4hStye6IKTuvFEDnffqQzD9iXCY+x4TdCIjwTa9JopFiMdQFG1Z2/kealYVSlnxfQ9aDQJL+03I4nJfptadp/jySka+/TZo0XlIqDBELOfATaSPtXW78FUma0CzF3Oh2pgNZsilX4FLe7kypQvWNISZLnV1lUc2o/EFNQUKj9llg2RacWq+sHY474EcNPhWTQkYXfD5iLbNcFdJZH1a9tEC1S3mdw==
Ve8Z4XqSrYeHYOs94zADcEmcJaseKPqIY2JWb8SgReYvraL4pEm8xZalXlU++loup52MQnA4hThm5PiTBdwlRY/zvTJmljQDgpFO89cs74ToQiUqpycxJMIsZ3QFH+ALIh/GPR0j2ZsqFUstJcrkONvJr7cIeZBLYqOb8UM3o6IV2LgWgCUJLhsgn8i6BQMw0wChoEM6nK0PT7OWQpgvqqmdHhJ8jn3rkifmMTLgF7s=
type GetReturnType<Type> = Type extends (...args: never[]) => infer Return
? Return
: never;
type Num = GetReturnType<() => number>;
type Str = GetReturnType<(x: string) => string>;
type Bools = GetReturnType<(a: boolean, b: boolean) => boolean[]>;
YGIlEt3zMp8aujO7k8VhzRGI9XsRMGVg8MGfzrE/8OmdaixIMHQ++AsNeicYbfX0ly7q78vNvy0y04Ro5iS5SKq2XdqOQedE8J6uVzc2USQbYloJuKA9Musf3Y8A6NxtMy06q37gj31qFdazIW9PIq1sisQI9k+IsnY8QaDScO1a7Aqp+RRJhnDctEd02YfB5+OaymhaO2CZhV+4nX9y+4CK/JkcgHX16x/O6XXxe10ws82Z1poSDYUgMIuABLb9ml5NqeqtW8eooy+P0xEMIPKcmOr22LzG6LLb9wbF/Z06ewOKcAL7rGTRk5Owy/8V
declare function stringOrNum(x: string): number;
declare function stringOrNum(x: number): string;
declare function stringOrNum(x: string | number): string | number;
type T1 = ReturnType<typeof stringOrNum>;
分布式条件类型
bZsP9PuIGAkg7cBiK6ltWUVi8vJe32JrOL/78TeQvDcaOaBtLsHCUzv72/9hTTHju+CscGL8pJADxahhmMgkufzpLaMm5oYWDaQwG3yGrZepvd7cg94/Qsc+DKdv3WMKCjtoVEYXsdjgMGCZAdh+36tWyXrR02aG38VKgUFvAFA=
type ToArray<Type> = Type extends any ? Type[] : never;
qUvCChYAg06qSFs3iLYe6J7ZESVQWMjIZbjiASlBZ8Ga8c37O0D6EGKHDEgOTRPjtPfy34X//I47XoQ/TjwQjSiIBdYh/tij/UeZXX83SMg3EI3HggzsCt5K5RkWMizzUrWFAB2KogEq0rsSQUmFGvjKX9a6ftN5u8cb8IzTt9o=
type ToArray<Type> = Type extends any ? Type[] : never;
type StrArrOrNumArr = ToArray<string | number>;
LWYfFMKpvZQWJKqgqs141fV8ry0VyEG2tQNCmTh3BYP+xpwobsiKbSEXiZIih6YRdZqVxuNhD/pjfBOQiIIxYA==
type StrArrOrNumArr =
string | number;
kT38YvFi971+EjeQ7CuG5qHvxIr98hEGbScnk++PniPqFJHYibk4DjhzXkkmcW6Kx4pdqKX2k7dthSCT+z6RPQ==
type ToArray<Type> = Type extends any ? Type[] : never;
type StrArrOrNumArr =
ToArray<string> | ToArray<number>;
2n7bFr1OOJXiS0Sx3QlwzIvicV60Qm9WtTn1CT1y0h0=
type StrArrOrNumArr =
string[] | number[];
tpo3MlXrvhTgpNi52dhcpdwe1Jvx/srhyEzoNdp/bVt5CMhpCKE1RX+/g6QvG9UyWv+Yszx4fxXvFMkqsjPxyfAakxlnozeprOpxHyPhj55PVhYT+ixqTrR+4gfqvmrkZC175taeRJCSdA2tGJuyCZTGlsZLuJv6WCpJ8BIM1cXJlhT6+c4IcmieSKvdwK6d
type ToArrayNonDist<Type> = [Type] extends [any] ? Type[] : never;
// 'StrArrOrNumArr' is no longer a union.
type StrArrOrNumArr = ToArrayNonDist<string | number>;