前言
在TypeScript中,有两中声明类型的方式。即Interface
与Type
。
interface X {
a: number
b: string
}
type X = {
a: number
b: string
};
上面两种方式均可,所以很让人困惑,到底
- 他们的区别是什么?
- 什么时候该使用type,什么时候该使用interface?
使用上的区别
可能这个问题实在困扰人,所以TS的官方文档都做出了解释:
Because an interface more closely maps how JavaScript objects work by being open to extension, we recommend using an interface over a type alias when possible.
interface通过扩展(符合开闭原则)javascript的方式,更贴合javascript的工作机制。所以推荐使用interface而不是type
下图中,interface可以被扩展,而type不能,只能使用联合体创建一个新的type。
但紧接着文档中又说道,在某些场合比如联合类型或者元组类型,可能还是需要使用type
On the other hand, if you can’t express some shape with an interface and you need to use a union or tuple type, type aliases are usually the way to go.
比如:
// 定义各一个基本数据类型的别名
type Primitive = number | string | boolean | null | undefined
// 为元组声明类型
type IData = [string, number, number];
总结来说,从官方的意思是,尽量使用interface
,在不得已的情况下再使用type
。
本质区别
官方的说法还算可以接受,但事实上,还有一个疑问,这也是本文想回答的问题,TypeScript为什么会有两种API去描述类型?
我个人感觉,这涉及到了TypeScript语言的类型系统本身,即TypeScript是一种Structual Type
,而不是Nonimal Type
。如果你了解Java,那么我可以告诉你TS中类型系统是区别于Java的。简单的说,Java中会通过接口的定义去描述类型(Nominal Type),而在TS中,类型的定义是依据于对象本身的行为。也就是所谓的鸭子类型(duck type)
在程序设计中,鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定
说的有些不好理解,不妨直接看下边的例子。
class Duck {
quack() {
console.log('鸭子在呱呱叫')
}
feathers() {
console.log('鸭子有白色的羽毛')
}
}
class Person {
quack() {
console.log('人在模仿鸭子叫')
}
feathers() {
console.log('人穿着羽绒服....')
}
}
const donald = new Duck();
const xiaoming = new Person();
const inTheForest = (duck: Duck) => {
duck.quack()
duck.feathers()
}
inTheForest(donald)
inTheForest(xiaoming) // 编译通过!!
在上面代码中,明明inTheForest
函数需要一只鸭子,你却给了一个人。但因这个人看起来像鸭子,所以,编译通过了…
事实上这并不奇怪,因为JS就是这样的。比如promise中的resolve
静态方法
返回一个状态由给定value决定的Promise对象。如果该值是thenable(即,带有then方法的对象),返回的Promise对象的最终状态由then方法执行决定
以上,即Promise.resolve函数的入参应该可以是一个Promise对象,但也可以是一个thenable
的对象,即我不关心你是什么类型,只要你有then方法就行。和我不关心你是不是鸭子,只要你能呱呱叫就行是不是很类似?
实际上,你可以认为JS是一个运行时的鸭子类型语言。而TS仅仅是把这个类型检验放到了编译期
所以在TS中,对于任意一个对象,包括基本数据类型,都隐式的含有自己的类型,因为在编译过程中需要动态的检查类型。这个类型是编译系统的,并且他对外暴露出来了,就是type
,所以type甚至可以表示基本数据类型。此外type的全名是叫type alias
,即声明他只是对当前对象已经存在类型的一个别名。
所以你应该能明白为什么会有两个类型声明的方式
- interface是正统的,静态类型语言都会包含的类型声明方式
- type是TS作为structual类型语言附带给你赠品。
下面的例子进一步验证了type是是TS类型系统的类型别名。注意注释的说明
以上,希望本文对你理解interface和type有所帮助。
参考文档
- Promise A+
- https://stackoverflow.com/questions/37233735/typescript-interfaces-vs-types
- https://www.w3cschool.cn/doc_flow/flow-lang-nominal-structural.html
- https://www.typescriptlang.org/docs/handbook/advanced-types.html#interfaces-vs-type-aliases
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise