如何在联合记录类型上使用Pick或Omit并保留类型校验?
问题:联合类型使用Pick/Omit后丢失类型校验的解决方案
问题描述
我有一个由不同记录类型组成的联合类型,这些记录类型拥有相同的键,但部分键的类型限制因记录类型而异。当对该类型使用Pick或Omit时,会丢失部分类型校验功能。
简化示例代码如下:
type A = { type: 'a', content: 'a-content' }; type B = { type: 'b', content: 'b-content' } type Full = {id: number} & (A|B); // 合法代码 const full: Full = { id: 1, type: 'a', content: 'a-content' } // 被正确标记为类型错误 const invalidFull: Full = { id: 1, type: 'a', content: 'b-content' } // 未被标记为类型错误,但content与type字段不匹配 const invalidFullPartial: Omit<Full, 'id'> = { type: 'a', content: 'b-content' }
请问如何设置才能在Full类型上使用Pick或Omit时,仍让TypeScript确保type和content的类型匹配?
解决方案
问题根源在于TypeScript内置的Pick/Omit默认不具备分布式处理能力,当交叉类型与联合类型混合时,无法保留联合成员内部的字段关联约束。以下是两种可行的解决方式:
方法1:调整类型定义顺序
先对原始联合类型应用Omit/Pick操作,再交叉额外字段类型,让联合类型的约束优先保留:
// 先定义联合类型,再交叉id字段 type Full = (A|B) & {id: number}; // 此时Omit<Full, 'id'>等价于A|B,自动保留type与content的关联约束 const invalidFullPartial: Omit<Full, 'id'> = { type: 'a', content: 'b-content' // 现在会被正确标记为类型错误 }
方法2:自定义分布式Pick/Omit工具类型
如果无法调整原始类型定义,可以自定义支持联合类型的工具类型,利用条件类型的分布式特性对每个联合成员单独处理:
// 自定义支持联合类型的Omit type DistributiveOmit<T, K extends keyof any> = T extends any ? Omit<T, K> : never; // 自定义支持联合类型的Pick type DistributivePick<T, K extends keyof T> = T extends any ? Pick<T, K> : never; // 使用自定义工具类型替代内置方法 const invalidFullPartial: DistributiveOmit<Full, 'id'> = { type: 'a', content: 'b-content' // 正确触发类型错误 }
原理说明
TypeScript的条件类型在处理联合类型时,会自动对每个联合成员单独执行条件逻辑。自定义的DistributiveOmit/DistributivePick正是利用这一特性,确保每个联合成员内部的字段关联约束被完整保留,而不是被扁平化处理导致约束丢失。
内容的提问来源于stack exchange,提问作者Drew Pereli
相关产品推荐
相关产品推荐

