可以毫不夸张的说,TypeScript是一个让你一旦用上以后会深度依赖且无法自拔的语言,不管在哪个团队做什么项目,只要我负责或者接触的项目哪怕磨破嘴皮子也会想办法用上TS。形容它是一个贴心的保姆一点不为过,也是我们代码质量的第一道护城河。
大名鼎鼎的Type 和 Interface 的区别是什么?
2024年了还有没被这个问题拷打过的前端同学吗?
面试官:type 和 interface 的区别是啥?
你:type可以做联合类型但没法实现继承,interce可以实现继承但没法实现联合类型
面试官:以后有机会再合作🙄
想起我偶像赵丽颖的一句话“不停打破与重塑自我”,也许终有一天我们会发现,每一次的成长一定伴随着一次自我的认知的粉碎与重塑!不管什么问题先问自己一句:真的是这样吗?有没有其它可能和答案?
显然是有的!它俩有着各自不同的宿命,interface是接口,用于描述一个对象,而type则见名知义,是类型别名,具体差异点如下:
两者相同点
- 都可以定义类型(原谅这是一句废话)
- 都允许被继承
我们定义一个 Person 类型和 Student 类型,Student 继承自 Person,可以有下面四种方式:
- interface 继承 interface
interface Person {
name: string
}
interface Student extends Person {
grade: number
}
- interface 继承 type
type Person = {
name: string
}
interface Student extends Person {
grade: number
}
- type 继承 type
type Person = {
name: string
}
// 使用交叉类型做继承
type Student = Person & {
grade: number
}
- type 继承 interface
interface Person {
name: string
}
// 使用交叉类型做继承
type Student = Person & {
grade: number
}
两者不同点
- type可以但interface做不到的:type可以定义基本类型、交叉类型、联合类型、元组类型
type Name = string // 基本类型
type arrItem = number | string // 联合类型
const arr: arrItem[] = [1,'2', 3]
type Person = {
name: Name
}
type Student = Person & { grade: number } // 交叉类型
type Teacher = Person & { major: string }
type StudentAndTeacherList = [Student, Teacher] // 元组类型
const list:StudentAndTeacherList = [
{ name: 'lin', grade: 100 },
{ name: 'liu', major: 'Chinese' }
]
- interface可以但type做不到的
- 合并重复申明:如果重复定义两个同名interface定义则其属性会被合并
interface Person {
name: string
}
interface Person { // 重复声明 interface,就合并了
age: number
}
const person: Person = {
name: 'lin',
age: 18
}
- type如果重复申明则会报错
Partial(可选属性,但仍然不允许添加接口中没有的属性)
interface IUser {
name: string
age: number
department: string
}
//经过 Partial 类型转化后得到
type optional = Partial<IUser>
// optional的结果如下
type optional = {
name?: string | undefined;
age?: number | undefined;
department?: string | undefined;
}
当我们不能明确地确定对象中包含哪些属性时,我们就可以通过Partial来声明。
Required(必选属性)
和Partial刚好相反,将一个定义中的属性全部变成必选参数
让一个类型的属性全部必填
Pick(部分选择)
可能需要从一个已声明的类型中抽取出一个子类型,在子类型中包含父类型中的部分或全部属性,这时我们可以使用Pick来实现,
ts中可以选择一个原来的接口中一部分的属性定义
interface User {
id: number;
name: string;
age: number;
gender: number;
email: string;
}
type PickUser = Pick<User, "id" | "name" | "gender">;
// 等价于
type PickUser = {
id: number;
name: string;
gender: number;
};
let user: PickUser = {
id: 1,
name: 'tom',
gender: 1
};
// 等价于
type PickUser = {
id: number;
name: string;
gender: number;
};
let user: PickUser = {
id: 1,
name: 'tom',
gender: 1
};
Omit(属性忽略)
与Pick相反,Pick用于拣选出我们需要关心的属性,而Omit用于忽略掉我们不需要关心的属性
interface User {
id: number;
name: string;
age: number;
gender: number;
email: string;
}
// 表示忽略掉User接口中的age和email属性
type OmitUser = Omit<User, "age" | "email">;
// 等价于
type OmitUser = {
id: number;
name: string;
gender: number;
};
let user: OmitUser = {
id: 1,
name: 'tom',
gender: 1
};
Readonly (只读属性)
如果要求对象中的一些字段只能在创建的时候被赋值,使用 readonly 定义只读属性(只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候)
interface Person {
readonly id: number;
name: string;
age?: number;
[propName: string]: any;
}
let tom: Person = {
id: 89757,
name: 'Tom',
gender: 'male'
};
tom.id = 9527; // Cannot assign to 'id' because it is a constant or a read-only property
本文写的比较早,先补充一部分内容,后期会渐进式整理其它TS相关的技术点,如果有不对的地方还请各位巨佬指正☕️
《重学JavaScript系列专栏》其它文章推荐:
- 发布订阅者模式原来是这样 “搞事情” 的!
- 脚本加载中 async 和 defer 到底啥区别?
- 你家3岁小孩也能看懂的防抖和节流
- 手动模拟实现call、apply和bind方法---call和apply方法实现
- 手写call、apply、bind方法---bind方法实现
《手撕源码系列专栏》文章推荐
《Webpack配置从基础到高阶系列专栏》文章推荐