首页 > 其他分享 >TS 自定义类型-修改使对象部分属性必填

TS 自定义类型-修改使对象部分属性必填

时间:2022-10-18 17:56:22浏览次数:74  
标签:自定义 必填 TS num str 类型 options 属性

工作中常常用 API 的入参是非必填的,而实例的属性因为有默认值而一定存在的情况,举个例子:

type TestOptions = {
  num?: number
  str?: str
  hookFn?: () => string
}

const defaultOptions = {
  num: 1,
  str: 'test'
} 

Class Test {
  options: TestOptions
  
  constructor (options: TestOptions) {
    this.options = Object.assign({}, defaultOptions, options)
  }
  
  excute () {
    this.options.num // error: 类型“boolean | undefined”的参数不能赋给类型“boolean”的参数。
  }
}

上述代码中,我们忽略 options 有开发者主动传入 { num: undefined } 的情况。实际上我们期望的结果是 this.options.num 一定会存在,不用每次都得加以判断,而 this.options.hookFn 倒是不一定存在。

也就是说我们需要改一下 this.options 的类型,思路是这样的:

class Test {
  options: Required<Pick<TestOptions, 'num' | 'str'>> & Omit<TestOptions, 'num' | 'str'>
}

解释:

  1. 从定义中选取 numstr 两个属性 Pick<TestOptions, 'num' | 'str'>
  2. 加 Required 使得 Pick 的新定义的属性全都要求必须存在
  3. Omit<TestOptions, 'num' | 'str'> 使从定义中排除 numstr 得到其他属性
  4. 将 2 和 3 两个对象连结起来得到新对象,新对象里 numstr 要求必须存在,而其他的属性依照原定义不做改变。

这么写过于啰嗦,所以用一个自定义的高级类型替代:

export type PickForRequired<T, K extends keyof T> = Required<Pick<T, K>> & Omit<T, K>

class Test {
  options: PickForRequired<TestOptions, 'num' | 'str'>
  
  excute () {
    // this.options.
    //              num
    //              str
    //              hookFn?
  }
}

最后完善下 this.options 赋值的逻辑,去规避 options 有开发者主动传入某属性为 undefined 的情况,借用 lodash 函数去合并对象:

this.options = _.assignWith({}, defaultOptions, options, function (objectVal, sourceVal) {
  return _.isUndefined(sourceVal) ? objectVal : sourceVal
})

参考

Ts高手篇:22个示例深入讲解Ts最晦涩难懂的高级类型工具

标签:自定义,必填,TS,num,str,类型,options,属性
From: https://www.cnblogs.com/everlose/p/16803492.html

相关文章