interface 与 type 的异同
interface
命令与type
命令作用类似,都可以表示对象类型。
很多对象类型既可以用 interface 表示,也可以用 type 表示。而且,两者往往可以换用,几乎所有的 interface 命令都可以改写为 type 命令。
它们的相似之处,首先表现在都能为对象类型起名。
type Country = {
name: string;
capital: string;
}
interface Country {
name: string;
capital: string;
}
上面示例是type
命令和interface
命令,分别定义同一个类型。
class
命令也有类似作用,通过定义一个类,同时定义一个对象类型。但是,它会创造一个值,编译后依然存在。如果只是单纯想要一个类型,应该使用type
或interface
。
interface 与 type 的区别有下面几点。
(1)type
能够表示非对象类型,而interface
只能表示对象类型(包括数组、函数等)。
(2)interface
可以继承其他类型,type
不支持继承。
继承的主要作用是添加属性,type
定义的对象类型如果想要添加属性,只能使用&
运算符,重新定义一个类型。
type Animal = {
name: string
}
type Bear = Animal & {
honey: boolean
}
上面示例中,类型Bear
在Animal
的基础上添加了一个属性honey
。
上例的&
运算符,表示同时具备两个类型的特征,因此可以起到两个对象类型合并的作用。
作为比较,interface
添加属性,采用的是继承的写法。
interface Animal {
name: string
}
interface Bear extends Animal {
honey: boolean
}
继承时,type 和 interface 是可以换用的。interface 可以继承 type。
type Foo = { x: number; };
interface Bar extends Foo {
y: number;
}
type 也可以继承 interface。
interface Foo {
x: number;
}
type Bar = Foo & { y: number; };
(3)同名interface
会自动合并,同名type
则会报错。也就是说,TypeScript 不允许使用type
多次定义同一个类型。
type A = { foo:number }; // 报错
type A = { bar:number }; // 报错
上面示例中,type
两次定义了类型A
,导致两行都会报错。
作为比较,interface
则会自动合并。
interface A { foo:number };
interface A { bar:number };
const obj:A = {
foo: 1,
bar: 1
};
上面示例中,interface
把类型A
的两个定义合并在一起。
这表明,interface 是开放的,可以添加属性,type 是封闭的,不能添加属性,只能定义新的 type。
(4)interface
不能包含属性映射(mapping),type
可以。
interface Point {
x: number;
y: number;
}
// 正确
type PointCopy1 = {
[Key in keyof Point]: Point[Key];
};
// 报错
interface PointCopy2 {
[Key in keyof Point]: Point[Key];
};
(5)this
关键字只能用于interface
。
// 正确
interface Foo {
add(num:number): this;
};
// 报错
type Foo = {
add(num:number): this;
};
上面示例中,type 命令声明的方法add()
,返回this
就报错了。interface 命令没有这个问题。
下面是返回this
的实际对象的例子。
class Calculator implements Foo {
result = 0;
add(num:number) {
this.result += num;
return this;
}
}
(6)type 可以扩展原始数据类型,interface 不行。
// 正确
type MyStr = string & {
type: 'new'
};
// 报错
interface MyStr extends string {
type: 'new'
}
上面示例中,type 可以扩展原始数据类型 string,interface 就不行。
(7)interface
无法表达某些复杂类型(比如交叉类型和联合类型),但是type
可以。
type A = { /* ... */ };
type B = { /* ... */ };
type AorB = A | B;
type AorBwithName = AorB & {
name: string
};
上面示例中,类型AorB
是一个联合类型,AorBwithName
则是为AorB
添加一个属性。这两种运算,interface
都没法表达。
综上所述,如果有复杂的类型运算,那么没有其他选择只能使用type
;一般情况下,interface
灵活性比较高,便于扩充类型或自动合并,建议优先使用。