TypeScript项目实践——在使用TypeScript中,如何减少类型的重复定义?

· 1310字 · 3分钟

TypeScript 项目实践 🔗

该系列文章主要是总结TypeScript在项目中实践经验,遇到什么样的问题,针对具体问题的解决方案,目的是给希望使用或正在使用TypeScript的同行提供参考,高效正确的使用TypeScript。

在使用TypeScript中,如何减少类型的重复定义? 🔗

后台管理系统,每个模块大致就是列表、详情、表单和一些操作。在定义Typescript的时候,往往会重复定义一些类似的结构。如何减少类型的重复定义呢?

模型已经完整定义,使用部分字段作为的新的类型 🔗

在定义列表的展示信息和详情的展示信息的时候,已经定义了一个比较完善的类。之后再去处理创建、编辑之类的操作可能只需要其中几个参数。


// 用户列表的信息
class Uesr {
  id: string;
  name: string;
  email: string;
  createTime: string;
}

例如:用户列表展示需要显示的信息比较完整,但是添加用户的时候可能只需要name、email。如果我们能把这两个属性摘出来就行了。内置的 Pick 类型,就可以完美解决我们的问题。

// 添加用户
function create(formValue: Pick<User, 'name' | 'email'>) {
  // todo:
}

// 为什么用 pick?我们看下Pick的实现原理
// 意思是选取指定一组属性,并组成一个新的类型
type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

或者,当我们创建的时候,表单中除了 User.id,其他都是必填项。但是重新定义一个新的不包含id的类型的话,又显得有些重复了。但是用 Pick 的话,需要写的 keys 又太多了。这个时候就需要用内置 Omit 来处理了。

// 添加用户
function create(formValue: Omit<User, 'id'>) {
  // todo:
}

// 我们来看为什么 Omit 能满足我们的需求
// Omit的原理就是从类型 T 中剔除 K 中的所有属性。
// 此处的 keyof any 而不是 keyof T, 我也是有点不明白,就去查了一下。简单来说就是社区选择的结果.
// 不明白的可以看 https://github.com/Microsoft/TypeScript/issues/30825
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

// 这里牵扯到 Exclude 类型,这里就先介绍下吧。
// 表示提取存在于 T,但不存在于 U 的类型组成的联合类型。
type Extract<T, U> = T extends U ? T : never;

注意:此处 keyof any 得到的是 string | nuber | symbol , 因为类型 key 的类型只能为 string | nuber | symbol 。

使用方法的参数类型,作为函数的返回值 🔗

某些时候我们在某个函数上定义的参数类型,可能是跟某些参数或者某些值的类型是相同的,这个时候我们就可以直接提取此函数的参数类型。具体实现就是使用 Parameters 类型。

例如,我们我们定义了一个格式化返回数据的函数,函数中我们定义了参数类型。然后我们的请求接口返回的数据与此相同。这个时候就能直接使用格式化函数的参数类型。

// 格式化返回数据的函数
function formatData(data: {
  id: string;
  name: string;
  e_mail: string;
  create_time: string;
}) { 
  // ... 
}

// 获取列表信息
function getPersonLists(): Parameters<typeof formatData>[0][] {
  // ...
}

我们通过下图可以看出, T0 此时的类型就是 formatData 中参数 data 的类型。

在线示例

将已存在的类型,组合成新类型 🔗

在项目中,有些是需要集合其他几个类型或者类型中的部分参数而成的新类型。

// 函数中的传参可能是需要多个类型中的参数,并且有些是可选项。
function create(options: Pick<User, 'name' | 'email'>
  & Partial<Pick<Department, 'department' | 'project'>>
  & Pick<UserRole, 'role' | 'type'>
) {
  // ...
}

// Partial 表示

代码中的 create 参数,需要 User 类型中的 name、email,需要 Department 中的 department、project,也需要 UserRole 中的 role、type。我们用 & 和 内置类型组合成一个新的类型。

总结 🔗

在写 Typescript 中,合理使用内置的类型,可以有效的减少重复定义类型,减少无意义的代码。当然,内置类型肯定是不限于我们所介绍的这些以及使用场景。并且我们当内置类型满足不了我们的需求的时候,我们也可以自定义类型。