相同点
不同点
Interface: 通过interface声明的接口,或者说声明的变量,它在声明的那一刻并不是最终类型,由于interface可以进行声明合并,所以它在创建后是可变的,可以将新成员添加到同一个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: () => "跑步" };
type TSearchFunc = () => string;
interface ISearchFunc {
(): string;
}
// 类型别名函数
const tSearchFunc: TSearchFunc = () => "张三";
// 接口函数
const iSearchFunc: ISearchFunc = () => "张三";
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 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 TPosition = "left" | "right" | "top" | "bottom";
let whichPosition: TPosition = "left";
type TValue = string | number | boolean;
let num: TValue = 18;
interface IDog {
name: string;
}
type TCat = {
age: number;
};
type Tuple = [IDog, TCat];
const tuple: Tuple = [{ name: "狗" }, { age: 18 }];
interface IPeople {
name: string;
}
interface IMan extends IPeople {
age: number;
}
type TPeople = {
name: string;
};
interface IMan extends TPeople {
age: number;
}
type TPeople = {
name: string;
};
// type交叉类型
type TMan = TPeople & { age: number };
interface IPeople {
name: string;
}
type User = IPeople & {
age: number;
};
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 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;
interface B {
age: number;
}
interface B {
key: string;
[key: string]: string;
}
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不能重新打开类型来添加新成员,而接口总是可以扩展的,即便这样官方还是建议我们尽量去使用接口代替类型别名。另一方面,如果你无法通过接口来描述一个类型并且需要使用联合类型或元组类型,这时通常会使用类型别名。