【问题标题】:Structural typing is not duck typing结构类型不是鸭子类型
【发布时间】:2018-10-11 14:09:58
【问题描述】:

TypeScript 中提到过handbook

TypeScript 的核心原则之一是类型检查侧重于值的形状。这有时被称为“鸭子类型”或“结构子类型”。在 TypeScript 中,接口扮演命名这些类型的角色,...


我的理解是,上面的核心原理与Duck typing无关,而是Structural typing,因为TypeScript是静态类型语言。

正如wiki 中提到的:它要求将类型检查推迟到运行时,并通过动态类型或反射来实现......对象的适用性取决于某些方法和属性的存在(具有适当的含义),而不是对象的实际类型。

上面的TypeScript核心原理怎么理解?

【问题讨论】:

  • This is sometimes called "duck typing"... 根据严格的定义,我没有看到 TypeScript 正式的 Duck Typing 系统的说法,并且快速的谷歌搜索显示了这个声明是真实的。 TypeScript 并不真正适合维基百科的文章,因为它根本不是一种运行时语言,它只是一个类型检查器。然而,它确实符合一些标准,包括基本的鸭子原理以及它编译为 JavaScript(一种动态类型语言)的事实。
  • @Aaron 类型检查侧重于 value 的形状,与结构类型有关,但与 Duck 类型无关。因为我对其含义的理解是……类型兼容性和等价性由类型的实际结构或定义决定。在手册中,它也说了同样的话...... 值得指出的是,类型检查器不要求这些属性以任何顺序出现,只需要接口所需的属性存在并且具有所需的属性输入。
  • 我认为您对duck-typing 使用的定义没有错,但在该术语的使用方式上并不完整,这就是TypeScript 有时被称为“duck typing”的原因。
  • @Aaron 由于在编译时与运行时进行类型检查,我看到下面的示例是这样的。1) let myObj:labelledValue = {size: 10, label: "Size 10 Object"}; 必须在编译时进行类型检查,遵循结构类型。 2) function printLabel(labelledObj: LabelledValue) {..}中接收到的参数对象只能在运行时检查,这里没有类型(function printLabel(labelledObj) {}),所以很明显JS遵循的规则是一样的(duck typing),在第二种情况。
  • 是的,如果你想说,TS 是“编译时鸭式”。

标签: javascript typescript type-systems duck-typing structural-typing


【解决方案1】:

来自Duck testDuck Typing 以及维基百科

如果它长得像鸭子,游泳像鸭子,叫起来像鸭子,那么它很可能就是鸭子。

这基本上就是 TypeScript 接口的工作方式。一个对象只需要看起来像接口,而不是显式地实现它。

形成您链接到的页面:

interface LabelledValue {
    label: string;
}

function printLabel(labelledObj: LabelledValue) {
    console.log(labelledObj.label);
}

let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);

尽管myObj 没有显式实现LabelledValue 接口,但它确实具有相同的结构,因此隐式实现了该接口。这是鸭子打字

更新 如果LabelledValue 接口被定义为一个类,那么上面的代码将不会“编译”。这是结构类型。

注意 TypeScript 没有运行时检查,因为它被“编译”为 JavaScript

【讨论】:

  • 1) Ducktyping 不需要两个对象具有相同的属性(相同类型和名称)。 Duck 类型不处理类型等价。对于一个对象,如果您调用的属性(例如方法)存在,那么鸭子类型表示您可以调用该方法。 2) 结构类型(至少在 TypeScript 中)根据这两个对象中成员的类型和名称处理两个对象的类型等价
  • 接口有点不同。如果一个对象具有与接口中定义的相同的属性和类型,则该对象将被接受为实现该接口,即使它没有明确定义为这样。
  • 如果LabelledValue 是一个类,那么这会改变事情。而myObj 不会通过测试,TypeScript 会出错
  • 这是一个不好的例子,我想说let myObj:labelledValue={...} 然后没有更多的麻烦,你不能再打电话给printLabel()。这就是interface关键字的目的之一,解决JS的此类问题
  • 在你的回答中:因此隐式实现接口对我来说看起来不正确。
【解决方案2】:
interface Duck {
    typing(): void;
}

const porky = {} as Duck;

try {
    porky.typing();
} catch (error) {
    console.log(error); // TypeError: porky.typing is not a function
}

const isDucky = (it: { typing?: () => void }): it is Duck =>
    typeof it.typing === 'function';

const daffy = {
    typing: () => console.log('tappity tap tap')
};

if (isDucky(daffy)) {
    daffy.typing(); // tappity tap tap
}

【讨论】:

    猜你喜欢
    • 2021-06-27
    • 2015-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-18
    • 2011-03-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多