将type和interface放在一起,是因为它们的某些行为很像,而区别又是一些完全不相关的特殊能力。
在进入本节整体之前也有一个概念要明确一下:
编程语言的子类型分为两种:名义子类型和结构子类型。名义子类型就是指,例如 Java 中类的继承,子类就是父类的子类型,而要建立父子类的关系只有一个办法就是extends(接口的implements也算),只有用了extends才会出现父子类型,也就是只能用extends才能让他们成为名义上的父子类型。结构子类型就是结构相同即可,而 TypeScript 就是结构子类型,即在 TypeScript 中
type Foo = {
a: string
}
和
type Bar = {
a: string
}
是一样的类型,不需要extends(尽管extends也可以创造子类型,但本质是结构相似)。所以
type Foo = {
a: string
}
type Bar = {
a: string,
b: number
}
中Bar是Foo的子类型。
相似的地方
对象自变量式的结构定义
interface Foo {
a: string
}
和
type Foo {
a: string
}
都是定义了一个有a属性的对象结构。
函数类型
函数类型其实由两个部分构成,参数类型和返回值类型。
interface Foo {
(a: string): string
}
和
type Foo = (a: string) => string
表示同一类型的函数,关于函数类型会在下一篇文章做详细介绍。
混合类型
由于JavaScript具有动态和灵活的性质,有时可能会遇到一个对象,该对象可以作为上述某些类型的组合使用,就是一个既具有函数特性又具有对象特性的类型,它具有一些属性。如下:
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
function getCounter(): Counter {
let counter = (function (start: number) { }) as Counter;
counter.interval = 123;
counter.reset = function () { };
return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
当然其中的
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
也可以使用type实现 :
type Counter = {
(start: number): string;
interval: number;
reset(): void;
}
关于|和&操作符的行为完全相同
type Foo = {
a: string
}
type Bar = {
b: number
}
type Baz = Foo & Bar
和
interface Foo {
a: string
}
interface Bar {
b: number
}
type Baz = Foo & Bar
的类型完全相同,同样|操作符也是。
在我看来,&和|操作符都会创建一个新的类型,而且是相关类型父子类型链上的类型。它们区别是他们的产物是在父子类型链上的不同角色。如上所示,第一段代码中,对Foo和Bar使用了&操作符,创建的新的类型Baz,结果是Baz是Foo和Bar的子类型,就是说,Bar类型的变量何以赋值给Foo和Bar类型的变量。在第二段代码中,对Foo和Bar使用了|操作符,创建的新的类型Baz,结果是Baz是Foo和Bar的父类型,即,Foo和Bar类型的变量可以赋值给Bar。
可选属性
interface Foo {
a?: string
}
和
type Foo = {
a?: string
}
都表示a可有可无。
可选属性需要注意的地方是a?: string并不等于a: string | undefined,如果想知道为什么,可以看这个 issue 32695 。
索引类型
interface Foo {
[x: string]: number
[x: number]: string
}
和
type Foo = {
[x: string]: number
[x: number]: string
}
都表示索引为string的属性的类型都为number,索引为number的属性的类型都为string。
用于类implements
interface ClockInterface {
currentTime: Date;
}
class Clock implements ClockInterface {
currentTime: Date = new Date();
constructor(h: number, m: number) { }
}
在 TypeScript 中,implements操作只是为class提供一种类型约束,没有其他作用,所以,主要是符合class结构的类型都是可以被implements的。所以除了interface之外,type也可以进行implements。但因为 JavaScript 中引入class是照着 OOP 来做的,所以在需要implements时,使用interface有更强的语意性。
不同的地方
type
(1) 类型别称
很简单,就是为已经存在的类型创建另一个名字,代表完全相同的意义。例如:
type ObjectAlias = object
虽然说这个特性是type独有的,但当原类型不是原始类型时,即原类型不是number、string、boolean、object、symbol、null、undefined、void、never、unknown、any时,interface可以使用以下方式实现类似的功能:
interface Foo {
a: string
}
interface FooAlias extends Foo {}
interface
(1) 扩展接口(extends interface)
interface Foo {
a: string
}
interface Bar extends Foo {
b: number
}
这里使用&可以实现类似的效果,如下:
interface Foo {
a: string
}
type Bar = Foo & {
b: number
}
(2) 扩展类(extends class)
class Control {
private state: any;
}
interface SelectableControl extends Control {
select(): void;
}
作者:M.TQ 链接:zhuanlan.zhihu.com/p/92906055\
在类声明时,其实也同时声明了一个描述该类结构和类型的接口,所以这里的扩展操作与上面(1)中的相似。
本文结束。