TS类型体操之Partial、Required、Pick、Omit、Readonly...

847 阅读4分钟

可以毫不夸张的说,TypeScript是一个让你一旦用上以后会深度依赖且无法自拔的语言,不管在哪个团队做什么项目,只要我负责或者接触的项目哪怕磨破嘴皮子也会想办法用上TS。形容它是一个贴心的保姆一点不为过,也是我们代码质量的第一道护城河

大名鼎鼎的Type 和 Interface 的区别是什么?

2024年了还有没被这个问题拷打过的前端同学吗?

面试官:type 和 interface 的区别是啥?

:type可以做联合类型但没法实现继承,interce可以实现继承但没法实现联合类型

面试官:以后有机会再合作🙄

想起我偶像赵丽颖的一句话“不停打破与重塑自我”,也许终有一天我们会发现,每一次的成长一定伴随着一次自我的认知的粉碎与重塑!不管什么问题先问自己一句:真的是这样吗?有没有其它可能和答案?

显然是有的!它俩有着各自不同的宿命,interface接口,用于描述一个对象,而type则见名知义,是类型别名,具体差异点如下:

两者相同点

  1. 都可以定义类型(原谅这是一句废话)
  2. 都允许被继承

我们定义一个 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做不到的
  1. 合并重复申明:如果重复定义两个同名interface定义则其属性会被合并
interface Person {
    name: string
}

interface Person {         // 重复声明 interface,就合并了
    age: number
}

const person: Person = {
    name: 'lin',
    age: 18
}

  1. 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系列专栏》其它文章推荐:

《手撕源码系列专栏》文章推荐

《Webpack配置从基础到高阶系列专栏》文章推荐