相同点
- 都可以描述 对象、函数,都可以扩展
不同点
- 接口可以 extends 和 implements ,type则不可以(type可以通过交叉类型实现 extends 行为)
- 接口可以声明合并,type则不可以
- type 可以声明基本类型别名,联合类型,元组等类型
- type 可以定义字符串字面量联合类型
- type 可以定义声明联合类型
- type 可以定义元组每个位置的类型
Interface: 通过interface声明的接口,或者说声明的变量,它在声明的那一刻并不是最终类型,由于interface可以进行声明合并,所以它在创建后是可变的,可以将新成员添加到同一个interface中
Type: 类型别名不会创建类型,它只是创建了一个新名字来引用那个类型,其实就是一个对象字面量类型,所以类型别名一旦声明就无法更改它们
Interface 与 Type 定义对象
type TUser = {
name: string;
age: number;
hobby: () => string;
};
interface IUser {
name: string;
age: number;
hobby: () => string;
}
// 类型别名对象
const tUser: TUser = { name: "张三", age: 18, hobby: () => "打篮球" };
// 接口对象
const iUser: IUser = { name: "张三", age: 18, hobby: () => "跑步" };
Interface 与 Type 定义函数
type TSearchFunc = () => string;
interface ISearchFunc {
(): string;
}
// 类型别名函数
const tSearchFunc: TSearchFunc = () => "张三";
// 接口函数
const iSearchFunc: ISearchFunc = () => "张三";
Interface 扩展(extends和 implements)
interface IPeople {
name: string;
}
interface IMan extends IPeople {
age: number;
}
class User implements IMan {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
const man1: IMan = { name: "张三", age: 18 };
const user1: IMan = new User("张三", 18);
接口可以通过继承接口进行扩展,类可以实现接口,当然接口也可以通过继承Type进行扩展,后面我们再举例说
Type 扩展 (通过交叉类型&实现)
type TPeople = {
name: string;
};
// type交叉类型
type TMan = TPeople & { age: number };
class User2 {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
const man2: TMan = { name: "张三", age: 18 };
const user2: TMan = new User2("张三", 18);
接口的声明合并
interface IPeople {
name: string;
}
interface IPeople {
age: number;
}
let user: IPeople = { name: "张三", age: 18 };
如果多次声明同一个同名接口,TS会将它们合并到一个接口中,而type则会报错
Type 定义字符串字面量联合类型
type TPosition = "left" | "right" | "top" | "bottom";
let whichPosition: TPosition = "left";
type TValue = string | number | boolean;
let num: TValue = 18;
Type 定义元组类型
interface IDog {
name: string;
}
type TCat = {
age: number;
};
type Tuple = [IDog, TCat];
const tuple: Tuple = [{ name: "狗" }, { age: 18 }];
Interface 与 Type 混合使用
Interface extends Interface
interface IPeople {
name: string;
}
interface IMan extends IPeople {
age: number;
}
Interface extends Type
type TPeople = {
name: string;
};
interface IMan extends TPeople {
age: number;
}
Type 与 Type 交叉类型
type TPeople = {
name: string;
};
// type交叉类型
type TMan = TPeople & { age: number };
Type 与 Interface 交叉类型
interface IPeople {
name: string;
}
type User = IPeople & {
age: number;
};
类可以实现Interface 以及 Type(除联合类型外)
interface IPeople {
name: string;
}
interface IMan extends IPeople {
age: number;
}
// interface extends type
type TPeople = {
name: string;
};
// type交叉类型
type TMan = TPeople & { age: number };
class User1 implements IMan {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
class User2 implements TMan {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
const user2 = new User2("张三", 18);
注意: 类可以实现Type,但是如果Type是联合类型则不行,因为如果是联合类型,此时类并不知道要实现那个类型
Interface 与 Type 赋值问题
interface StringMap {
[key: string]: string;
}
let props: StringMap;
type A = {
key: string;
};
interface B {
key: string;
}
const a: A = { key: "1" };
props = a;
const b: B = { key: "1" };
// 报错:不能将类型“B”分配给类型“StringMap”,类型“B”中缺少类型“string”的索引签名
props = b;
问题:为什么 props = b 会报错
- 类型别名一旦创建就不会更改,所以类型已经固定,key值肯定是string类型
- Interface可以进行声明合并,所以它在创建后是可变的,可以将新成员添加到同一个interface中(如下方添加age字段),此时b肯定不能赋值给props,所以需要添加 索引签名 来约束 interface B,添加约束后,b中所有的属性值肯定是string类型,此时就会与StringMap兼容,所以就不会报错了
interface B {
age: number;
}
interface B {
key: string;
[key: string]: string;
}
Interface 与 Type 扩展注意事项
interface IPeople {
name: string;
age: number
}
// 后续属性声明必须属于同一类型。属性“age”的类型必须为“number”,但此处却为类型“string”。
interface IMan extends IPeople {
name: string;
age: string;
}
接口继承时,后续属性声明必须和继承的属于同一类型,否则在声明时就会报错
type IPeople = {
name: string;
age: number;
};
type IMan = IPeople & {
name: string;
age: string;
};
通过Type扩展时,虽然IPeople 和 IMan中定义的age类型不同,但是定义时不会报错,当我们使用时才会报错
// 不能将类型“number”分配给类型“never”
let obj1: IMan = { name: "张三", age: 18 };
官网说
几乎所有interface具有的功能,type都可以实现,主要区别在于type不能重新打开类型来添加新成员,而接口总是可以扩展的,即便这样官方还是建议我们尽量去使用接口代替类型别名。另一方面,如果你无法通过接口来描述一个类型并且需要使用联合类型或元组类型,这时通常会使用类型别名。