thumbnail
TypeScript是一种静态类型的编程语言,它是JavaScript的超集。它为JavaScript添加了静态类型、类、接口和模块等功能,并通过编译过程将其转换为纯JavaScript代码。
SCOTT STUDIO

需求描述

在我们使用ts日常开发中,经常会遇到这样一种场景,以代码为例:

interface Article {
  title: string
  content: string
  createTime: Date
  author: string
  readCount: number
}

function createArticle (options: Article) { ... }

假设我要做一个文章的创建,文章的对象有Article类型内的这些属性,然后我用一个函数来创建文章,函数的参数是一个配置信息,配置信息的约束为Article类型。

但是,我们在创建文章的时候,有的信息是可以不传递的,比如readCount可以默认为0;createTime可以默认是当前时间;author可以默认是匿名,那么这个时候,很显然Article类型就不满足我们的需求了。

该怎么解决这种情况呢?我们知道,在ts类型中,给字段加上 ? 可以表示可选,那么也许你会想到,将Article类型改造成下面这样

interface Article {
  title: string
  content: string
  createTime: Date
  author: string
  readCount: number
}

interface CreateArticleOptios {
  title: string
  content: string
  createTime?: Date
  author?: string
  readCount?: number
}

function createArticle (options: CreateArticleOptios) { ... }

这样做看上去满足了我们的需求,实际上这样是不可取的,因为Article类型是用来约束文章的,文章中的这些信息肯定是必须的,不能为空,只是说创建的时候参数是可选的。

而且,这样做也会造成重复代码,会给后期维护增加成本,如果哪一天文章的类型字段增加或者减少了,都会导致需要对CreateArticleOptios类型同时进行维护,所以这种办法也是不可取的。

如何解决?

所以我们要如何处理这种需求呢?其实很简单,同样的还是要创建一个新的类型,但是这个新的类型需要是根据Article类型运算出来的类型。先贴代码:

interface Article {
  title: string
  content: string
  createTime: Date
  author: string
  readCount: number
}

type ArticleOptional = Optional<Article, 'createTime | author | readCount'>;
 
function createArticle (options: ArticleOptional) { ... }

也就是说,我们希望有这么一种运算(在上述代码中为Optional),传入一个类型,和需要变成可选的字段,它会自动帮我们生成一个对应的新类型。

实现Optional

先上代码

...
type Optional<T, K extends keyof T> = Omit<T, K> & Partial<pick<T, K>>;

type ArticleOptional = Optional<Article, 'createTime | author | readCount'>;
...

我们一步步来解释,首先&是Typescript中的交叉类型,看下面的例子:

interface obj1 {
  a: string
  b: string
}

interface obj2 {
  c: string
  d: string
}

type obj3 = obj1 & obj2

const obj: obj3 = { 
  a: 'a',
  b: 'b',
  c: 'c',
  d: 'd'
}

假如obj2中的c,d都是可选的,那么交叉出来的obj3中的c,d也会是可选的,那么这正是我们想要的。

那么是不是可以理解成,在我们创建的Optional类型中,&前面的部分把 'createTime | author | readCount' 去除掉,只保留没有传递过来的字段,然后&后面的部分,把传进来的字段变成可选,两者交叉之后,就能得到我们想要的结果。

Typescript中的Omit工具方法

Omit是ts中给我们提供的一个工具方法,它的作用很简单,传入两个参数,第一个参数是类型,第二个参数是类型里的字段,它会把传入的字段从类型中去掉。

interface Todo {
  title: string
  content: string
  createdAt: number
  date: Date
}

type TodoOmited = Omit<Todo, 'date | content'>

const todo1: TodoOmited = {
  title: 'title',
  createdAt: 1615544252617
} 

Typescript中的Pick工具方法

Pick同样是ts中给我们提供的工具方法,它的作用是从类型中提取出传入的字段参数。

interface Todo {
  title: string
  content: string
  createdAt: number
  competed: boolean
}

type TodoPicked = Pick<Todo, 'competed | content'>

const todo1: TodoOmited = {
  competed: true,
  content: 'content'
} 

Partial的作用,则是把类型中的所有字段都变成可选。

所以最终我们得到的是左边不需要可选的字段和右边需要可选的字段进行交叉,从而得到了我们想要的结果。

总结

在使用ts开发的时候,类似的场景非常之多,我们需要在这一个例子中,学会利用类型体操去解决我们实际开发中遇到的各种需求。

最后~如果本文对你有所帮助,可以点个赞或者请我喝杯奶茶~万分感谢🎉🎉🎉