与 Typescript 中的 "extends" 关键字混淆

Confusion with the "extends" keyword in Typescript

想想 Typescript 中的以下代码行:

let x: 'a' | 'b' extends 'a' ? true : false;

我想知道 x 的类型是否正确,因为直觉上 'a' | 'b''a' 扩展 版本(至少我的直觉是这样说的)。我认为 extends 会像数学中的子集一样工作。 A extends B 当且仅当 B ⊆ A.

不过,这里的实际类型x好像是false。我想我不明白 extends 关键字是如何工作的。

扩展检查实例的类型,不检查可能的变量。即使它有效,对于像 类 这样的复杂类型也毫无意义。如果您使用 String 检查它,它将是正确的。

let x: ('a' | 'b') extends String ? true : false;

输出将是;

true

您可以查看 https://www.typescriptlang.org/docs/handbook/2/conditional-types.html

中有关条件类型的文档

根据 Liskov Subsitution Principle,如果“Y extends X”或等同于“Y 是 X 的子类型”,那么只要请求符合类型 X 的值,就可以使用符合类型 Y 的值。这导致了一个违反直觉的结论:当涉及可能值的范围时,如果 Y 扩展 X,则 Y 比 X 更受约束。所有 Y 都是 X,但并非所有 X 都是 Y。

在您的示例中,因为 'a' | 'b' 可以是 'a''b',联合类型不会扩展类型 'a',因为 'b' 不会' 替代 'a'。相反,'a' extends ('a' | 'b'),因为所有匹配 'a' 的值都可以在请求 'a' | 'b' 的地方工作。

因此,A extends B 当且仅当 A ⊆ B.

这在 TypeScript 中不太直观的一个原因是您的示例处理的是文字值的并集。我们从对象的角度来考虑这个问题可能更有意义,其中 {foo: number, bar: number} extends {foo: number}。后者 {foo: number} 可以有任何类型的 bar 属性 或根本没有 bar。前者 {foo: number, bar: number} 更具体,更受约束:不仅 foo 是一个数字,而且 bar 也是一个数字。这也匹配 class 或接口定义中 extends 的使用:subclass 或子接口 添加 属性和方法,其中 与超级class或超级接口相比,进一步限制了实例。

这也是: never is the most constrained type because no actual values match it, so never extends everything. The empty set is a subset of every set; the empty type never is the subtype of every type and can be assigned to every other type

type Foo = { foo: number };
type Bar = never extends Foo ? true : false;  // true