如何在TypeScript的omit()函数泛型中保留特定string[]类型
解决方案
你可以通过TS 5.0新增的const类型参数修饰符解决这个问题,它会强制TypeScript不拓宽传入的字面量类型,完整类型定义如下:
declare function omit< O extends Record<string, unknown>, const P extends string | readonly string[] >( source: O, props: P ): Omit<O, P extends string ? P : P[number]>
测试效果完全符合预期:
const o1 = omit({ a: 1, b: 'foo', c: true }, 'b'); o1.a // 正常 o1.b // 报错 o1.c // 正常 const o2 = omit({ a: 1, b: 'foo', c: true }, ['b', 'c']); o2.a // 正常 o2.b // 报错 o2.c // 报错
如果需要兼容TS 5.0以下的版本,也可以用函数重载实现,用户传入数组参数时手动加as const即可:
declare function omit<O extends Record<string, unknown>, P extends string>(source: O, props: P): Omit<O, P> declare function omit<O extends Record<string, unknown>, P extends readonly string[]>(source: O, props: P): Omit<O, P[number]> // 使用时 const o2 = omit({ a: 1, b: 'foo', c: true }, ['b', 'c'] as const);
原理说明
为什么普通数组参数会丢失字面量类型?
TypeScript默认会对字面量数组做类型拓宽,比如['b', 'c']这种字面量默认会被推导为通用的string[]类型,而不是['b', 'c']元组类型,所以P[number]就会被推导为string,导致Omit把对象所有属性都移除。为什么剩余参数可以保留字面量类型?
这是TypeScript的设计特殊处理:剩余参数的作用是收集零散传入的单个参数,TS默认就会保留每个参数的字面量类型,合并为字面量元组,不会做自动拓宽,所以能准确拿到所有要删除的属性的联合类型。
内容的提问来源于stack exchange,提问作者shau-kote
相关产品推荐
相关产品推荐

