【问题标题】:Confusion with the "extends" keyword in Typescript与 Typescript 中的“扩展”关键字混淆
【发布时间】:2022-01-02 20:26:02
【问题描述】:

想想 Typescript 中的以下代码行:

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

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

但是,这里的x 的实际类型似乎是false。我想我不明白 extends 关键字究竟是如何工作的。

【问题讨论】:

  • 如果Y 扩展X 那么你可以在需要X 的地方替换Y。您可以在需要'a' 的地方替换'b' 吗?
  • @JeffBowman 那么,你的意思是我的直觉 (B ⊆ A) 实际上应该转向另一个方向 (A ⊆ B) 说A extends B
  • 超类有点像子类的子集(撇开抽象类不谈)——但一个字符不能是同一类型的两个不同字符的子集。

标签: javascript typescript types


【解决方案1】:

根据Liskov Subsitution Principle,如果“Y 扩展 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 iff A ⊆ B

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

这也是why never is assignable to everythingnever 是最受限制的类型,因为没有实际值匹配它,所以never 扩展了所有内容。 The empty set is a subset of every set;空类型never 是每个类型和can be assigned to every other type 的子类型。

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

【讨论】:

  • 完美答案。谢谢。
【解决方案2】:

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

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

输出将是;

true

您可以查看来自https://www.typescriptlang.org/docs/handbook/2/conditional-types.html 的有关条件类型的文档

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-02-28
    • 1970-01-01
    • 2018-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多