【问题标题】:Why does Typescript allow for subtyping?为什么 Typescript 允许子类型化?
【发布时间】:2019-08-06 09:32:38
【问题描述】:

根据文档,“TypeScript 中的类型兼容性基于结构子类型”。所以这是可能的:

type Person: {
  name: string;
}

const developer = {
  name: 'Joe',
  language: 'Typescript',
}

// this is ok because Person is a subtype of typeof developer
const otherDeveloper: Person = developer; // who writes code like that?!

这有很多后果,其中之一是您在使用 Object.keys 时会丢失类型信息:

// "keys" type is array of strings not `name` literal as this would be of course wrong because in runtime "keys" is ['name', 'language']
const keys = Object.keys(otherDeveloper); 

所以我试图按照他们的承诺在 TS 文档中找到这种子类型化的原因,但我找不到

仔细考虑了 TypeScript 允许不良行为的地方,在本文档中,我们将解释这些情况发生的位置以及它们背后的动机。

这可能对我有帮助的唯一地方是一个期望更窄类型对象的函数,例如:

function getName(person: Person) {
  return person.name;
}

getName(developer); // works fine because of subtyping

如果您在这种情况下必须使用强制转换,我个人认为这不是什么大问题:

getName(developer as Person);

还有其他我可能遗漏的例子吗?

【问题讨论】:

  • 嗯,这称为多态性,是所有面向对象语言的基本原则。它基本上模拟了生活的现实。任何人都可以注册一个节目。开发人员是一个人。他/她比“基本”人拥有更多的属性(例如他/她知道的计算语言)。但节目不在乎。它只关心你是否是一个人。
  • 如果没有子类型化,什么都不会真正起作用,它是任何面向对象方法的基础,即使在 JS 中,您也经常将具有更多字段的对象传递给仅使用少数字段的函数。奇怪的部分是当 TS 不允许子类型替换基本类型时,例如当它警告过多的属性检查时(getName( { name: 'Joe', language: 'Typescript', }) 是一个错误)。过多的属性检查与 OO 相悖,但有助于发现常见错误。
  • @JBNizet 我认为这里所说的问题是没有为Developer 定义明确的子类,但代码仍然适用于developer 对象上的额外属性,即使该对象的类型为@987654329 @ 在强类型语言上会失败的东西。
  • @apokryfos 是的 .. 每个 C#/C++/Java 开发人员在迁移到 TS 时都会遇到结构化类型的有趣惊喜。它是强类型的,而不是您认为的强类型。

标签: typescript subtyping structural-typing


【解决方案1】:

Typescript 使用结构类型的原因是 JS 是鸭子类型。

所以你可以在 JS 中做上面写的事情,理想情况下你可以在 TS 中以更安全的方式来做。 Javascript不关心对象声明的类型,JS中没有这个概念,它只关心对象在运行时的属性。因此,任何对象都可以传递到您的 getName 函数中,只要 name 属性存在,该函数就可以正常运行。

此外,由于 JS 具有不属于特定类的对象字面量,因此很难在任何地方明确指定继承关系。明确类型关系会使 TS 对 JS 开发人员的吸引力降低。在结构类型系统下,类型大多适用于我们,您可以从中获得很多好处,而不必非常明确。

有一些方法可以通过使用私有属性 (ex) 或使用品牌类型 (ex) 来绕过结构类型并在 typescript 中模仿规范类型

【讨论】:

    猜你喜欢
    • 2018-07-27
    • 2020-08-29
    • 2021-05-30
    • 1970-01-01
    • 2019-06-09
    • 2010-10-04
    相关资源
    最近更新 更多