TypeScript"extends"条件类型语句仅在使用泛型表示时有效



我正在努力更好地了解TypeScript中的extends关键字及其潜在应用程序。

我遇到的一件事是两个内置的实用程序,ExtractExclude,它们同时利用了extends和Conditional Typeing。

/**
* Exclude from T those types that are assignable to U
*/
type Exclude<T, U> = T extends U ? never : T;
/**
* Extract from T those types that are assignable to U
*/
type Extract<T, U> = T extends U ? T : never;

我四处玩耍是为了更好地理解这个";"缩小";,或者更好地说";子集滤波";工作,并尝试创建自己的实现,只是为了看到它的实际应用,但遇到了这种非常奇怪的行为:

游乐场链接示例:

type ValueSet = string | 'lol' | 0 | {a: 1} | number[] | 643;

type CustomExclude<T, U> = T extends U ? T : never;
// this works:
// type Result1 = 0 | 643
type Result1 =  CustomExclude<ValueSet, number>; 

// but this doesn't?
// type Result2 = never
type Result2 = ValueSet extends number ? ValueSet : never;

为什么会发生这种情况?

我希望这两个实例都返回正确的类型子集,但条件类型只有在通过泛型表达时才有效。

有人能给我解释一下这背后的逻辑吗?

请参阅分布式条件类型:

当条件类型作用于泛型类型时,当给定联合类型时,它们将变为分布式类型。例如,以以下内容为例:

type ToArray<Type> = Type extends any ? Type[] : never;

如果我们将联合类型插入ToArray,则条件类型将应用于该联合的每个成员。

type ToArray<Type> = Type extends any ? Type[] : never;

type StrArrOrNumArr = ToArray<string | number>; // string[] | number[]

因此,如果将extends与泛型类型一起使用,则整个条件类型将应用于并集中的每个元素。

如果将extends与非泛型类型一起使用,就像在第二个示例中使用的那样,条件类型将应用于整个类型。

您甚至可以在第一个示例中关闭分布性。只需将你的仿制药包装在方括号中:

type ValueSet = string | 'lol' | 0 | {a: 1} | number[] | 643;

type CustomExclude<T, U> = [T] extends [U] ? T : never;
// never
type Result1 =  CustomExclude<ValueSet, number>; 

包装在方括号中的泛型被视为非泛型类型,就像您的第一个示例中一样。

在实践中,这种模式非常有用。使用T extends any只是为了打开分发性是很常见的。

假设您有某种对象类型。您希望获得所有密钥并对其应用一些修饰符。换句话说,绘制它们的地图。考虑这个例子:

type Foo = {
name: string;
age: number
}
// non verbose approach, distributivity
type ChangeKey<T> = keyof T extends string ? `${keyof T}-updated` : never
type Result = ChangeKey<Foo>

// middle verbose approach
type ChangeKey1<T> = {
[Prop in keyof T]: Prop extends string ? `${Prop}-updated` : never
}[keyof T]
type Result1 = ChangeKey1<Foo>
// verbose approach
type ChangeKey2<T extends Record<string, unknown>> = keyof {
[Prop in keyof T as Prop extends string ? `${Prop}-updated` : never]: never
}
type Result2 = ChangeKey2<Foo>

正如您可能已经注意到的,ChangeKey比其他产品更优雅。

第二段代码正在进行一次检查,以查看整个类型是否从数字扩展。如果是,则返回整个类型,否则返回never。带有泛型的版本将遍历并集中的所有单个类型(首先是string,然后是"lol",然后是0等),并对它们进行单独评估。然后你得到了一个幸存下来的任何一种类型的联盟。

从第二个例子中可以得到一个非never值,但前提是每个可能的值都是一个数字。例如:

type Example = 1 | 3 | 5;
type Example2 = Example extends number ? Example : never;
//  Example2 is 1 | 3 | 5

相关内容

  • 没有找到相关文章

最新更新