结构化类型系统
如果为 Cat 类新增一个独特的方法,这个时候的表现才是符合预期的,即只能用真实的 Cat 类来进行调用:
class Cat {
meow() { }
eat() { }
}
class Dog {
eat() { }
}
function feedCat(cat: Cat) { }
// 报错!
feedCat(new Dog())
TypeScript 比较两个类型并非通过类型的名称(即 feedCat 函数只能通过 Cat 类型调用),而是比较这两个类型上实际拥有的属性与方法。
最初的例子里,Cat 与 Dog 类型上的方法是一致的,所以它们虽然是两个名字不同的类型,但仍然被视为结构一致,这就是结构化类型系统的特性。
结构类型的别称为鸭子类型(Duck Typing),这个名字来源于鸭子测试(Duck Test)。其核心理念是,如果你看到一只鸟走起来像鸭子,游泳像鸭子,叫得也像鸭子,那么这只鸟就是鸭子。
但如果为 Dog 类型添加一个独特方法呢?
class Cat {
eat() { }
}
class Dog {
bark() { }
eat() { }
}
function feedCat(cat: Cat) { }
feedCat(new Dog())
这个时候为什么没有类型报错了?
结构化类型系统认为 Dog 类型完全实现了 Cat 类型。至于额外的方法 bark,可认为是 Dog 类型继承 Cat 类型后添加的新方法,即此时 Dog 类可以被认为是 Cat 类的子类。
更进一步,在比较对象类型的属性时,同样会采用结构化类型系统进行判断。而对结构中的函数类型(即方法)进行比较时,同样存在类型的兼容性比较:
class Cat {
eat(): boolean {
return true
}
}
class Dog {
eat(): number {
return 18;
}
}
function feedCat(cat: Cat) { }
// 报错!
feedCat(new Dog())
这是结构化类型系统的核心理念,即基于类型结构进行判断类型兼容性。
严格来说,鸭子类型系统和结构化类型系统并不完全一致,结构化类型系统意味着基于完全的类型结构来判断类型兼容性,而鸭子类型则只基于运行时访问的部分来决定。
如果调用了走、游泳、叫这三个方法,那么传入的类型只需要存在这几个方法即可(而不需要类型结构完全一致)。
由于 TypeScript 本身并不是在运行时进行类型检查,同时官方文档中同样认为这两个概念是一致的(One of TypeScript’s core principles is that type checking focuses on the shape that values have. This is sometimes called “duck typing” or “structural typing”.)。因此可以直接认为鸭子类型与结构化类型是同一概念。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论