【问题标题】:Why this CustomExtract returns an different result from default Extract?为什么此 CustomExtract 返回与默认 Extract 不同的结果?
【发布时间】:2023-01-22 18:48:03
【问题描述】:
代码解释了我的问题:
type A = {
a: number,
} | null
// Extract as defined in lib.es5.d.ts
type Extract<T, U> = T extends U ? T : never;
type CustomExtract = A extends null ? A : never;
type Result1 = Extract<A, null> // null
type Result2 = CustomExtract; // never
Extract 和 CustomExtract 是相同的代码,区别在于 Extract 是泛型类型。
此外,作为相关示例,string | null 不会扩展 null。
那么,在这个主题中,类型到底是如何工作的呢?我可以想象它可能会为每种类型的联合运行通用类型,然后合并所有结果,但我想要真正的技术定义和它的工作。
【问题讨论】:
标签:
typescript
typescript-generics
【解决方案1】:
区别在于 Extract 是 distributive conditional type,而你的 CustomExtract 不是。
为了使 X extends Y ? A : B 形式的条件类型具有分配性,检查的类型 X 必须是“裸类型参数”;这是一个类型参数比如Tinterface Foo<T> {...},和裸因为它只是被检查的类型参数(即T extends ...),而不仅仅是一些包含类型参数的表达式(例如Promise<T> extends ...或[T] extends ...)。
正如您推测的那样,分布式条件类型确实评估为检查类型 T 的每个联合元素的条件联合。因此,如果 F<T> 是分配条件类型,则 F<A | B | C> 将被评估为 F<A> | F<B> | F<C>。一个潜在问题是 F<never> 将被评估为 never 无论 F 的详细信息是什么(只要它是分布式的),因为 never 被认为是 "empty union type"。
有关更多信息,请参阅我的other answer,了解什么是分布式条件类型以及它们如何工作。
【解决方案2】:
@jcalz 所说的都是正确的,但我会尽量用通俗易懂的英语表达出来。
Extract<T, U> 所做的是返回子集T 可分配给 U。如果 T 有一些 U 类型和一些其他类型,我们只得到 U 类型。尽管有一个看起来与CustomExtract 相同的定义,但它为何以及如何做到这一点,这就是@jcalz 的答案。
在您的例子中,type A 是{a: number} 和null 的并集。 {a: number}不可分配给null,但null可分配给null。所以可分配给null的子集是null。
type CustomExtract = A extends null ? A : never;永远不会返回一个子集.如果满足条件,则返回完整的A,如果不满足条件,则返回never。
为了使X extends Y 为真,X 的所有值都必须可分配给Y,换句话说,X 必须等于或小于Y。
A extends null 是错误的,因为 A 比 null 宽。另一方面,null extends A 是真实的。