给ts定义一个对部分字段设置成可空的工具类型

695 阅读2分钟

前言

在ts中,我们使用Partial<Type>的将一个类型的所有字段的类型转换成可空类型:

type A = {
  a: string
  b: number
  c: boolean
}

type B = Partial<A>

/**
 * type B = {
 *  a?: string
 *  b?: number
 *  c?: boolean
 * } 
 */

使用Required<Type>将一个类型的所有字段类型转换成非空类型:

type A = {
  a?: string
  b?: number
  c?: boolean
}

type B = Required<A>

/**
 * type B = {
 *  a: string
 *  b: number
 *  c: boolean
 * } 
 */

然而,ts中并没有可以将一个对象的部分字段类型设置为可空类型的工具类型

既然ts中没有提供,那我们就自己定义一个PartiallyOptional<Type, Keys>的工具类型。

限制Keys的类型

我们使用PartiallyOptional<Type, Keys>时,只希望Keys的值只能是Type的字段的字符串,所以Keys只能是Type类型中,字段名组成的枚举类型。ts提供了keyof关键字来获取一个类型的所有字段,并组合成字符串枚举:

type A = {
  a: string
  b: number
  c: boolean
}

type B = keyof A

/**
 * type B = "a" | "b" | "c"
 */

type PartiallyOptional<Type, Keys extends keyof Type>

挑选出指定字段的类型

我们需要对某些字段设置为可空,就需要先将这些字段挑选出来,ts提供了Pick<Type, Keys>类型来挑选一个类型中的指定字段,来构建出新类型:

type A = {
  a: string
  b: number
  c: boolean
}

type B = Pick<A, "b" | "c">

/**
 * type B = {
 *  b: number
 *  c: boolean
 * } 
 */

type PartiallyOptional<Type, Keys extends keyof Type> = Pick<Type, Keys>

将挑选字段的类型设置成可空

我们获取了需要的字段后,需要将他们设置成可空,这时就使用一开始提到的Partial<Type>类型了:

type PartiallyOptional<Type, Keys extends keyof Type>= Partial<Pick<Type, Keys>>

合并原来的类型

我们获取了一个所有字段是可选的类型后,就需要和原来的对象进行合并。在ts中,使用&来合并两个类型的字段:

type A = {
  a: string
  b: number
}

type B = {
  c: boolean
}

type C = A & B

/**
 * type C = {
 *  a: string
 *  b: number
 *  c: boolean
 * } 
 */
type PartiallyOptional<Type, Keys extends keyof Type>= Type & Partial<Pick<Type, Keys>>

排除原来类型中对应的字段

此时,感觉这个新类型好像已经完成了,但,当我们在使用的时候,发现制定的字段并没有转换成可空类型:

type A = {
  a: string
  b: number
  c: boolean
}

type B = PartiallyOptional<A, "c">


/**
 * type B = {
 *  a: string
 *  b: number
 *  c: boolean
 * } 
 */

原因是两个类型组合时,ts对于字段相同,类型不同的处理问题:

  • 一个非空类型组合一个可空类型时,则该类型变成非空类型

所以上面的挑选后的类型和原来的类型组合后,变成了非空类型。

所以我们不能单纯的组合,而是要先排除掉那些已经已经转换成可空的字段。ts中提供了Omit<Type, Keys>类型来对一个类型移除指定的字段,并将剩余字段组成新的类型。

type A = {
  a: string
  b: number
  c: boolean
}

type B = Omit<A, "b" | "c">

/**
 * type B = {
 *  a: string
 * } 
 */

我们将两个类型组合后,就是我们的新类型了:

type PartiallyOptional<Type, Keys extends keyof Type> = Omit<Type, Keys> & Partial<Pick<Type, Keys>>