TypeScript 如何从映射类型创建属性互斥的联合类型
解决方案
基于你提到的「所有子类型无重叠属性」的前提,我们可以通过把非当前子类型的所有属性标记为never实现互斥校验,不需要用户显式传入undefined,实现代码如下:
// 提取Map中所有子类型的所有属性名的联合 type AllValueKeys<T extends SomeMap> = T[keyof T] extends Record<any, any> ? keyof T[keyof T] : never type ExclusiveArgs<T extends SomeMap> = { // 遍历每个子类型,保留自身属性的同时,把其他所有子类型的属性设为可选never [K in keyof T]: T[K] & Partial<Record<Exclude<AllValueKeys<T>, keyof T[K]>, never>> }[keyof T] function bar<T extends SomeMap>(args: ExclusiveArgs<T>){ // 业务逻辑 }
效果验证
// 合法调用,无报错 bar<MyMap>({ x: 1, y: 2 }) bar<MyMap>({ a: 1 }) bar<MyMap>({ l: 1, j: 2, k: 3 }) // 混合不同子类型属性,报错:a的类型为never,无法赋值为1 bar<MyMap>({ x: 1, y: 2, a: 1 }) // 传入不存在的属性,报错:z不存在于类型中 bar<MyMap>({ z: 5 })
原方案问题说明
你之前的实现把其他子类型的属性设为了undefined,TS不会对未显式声明的可选undefined属性做自动兼容,所以要求你必须手动把所有其他属性都赋值为undefined才能通过校验。而本方案用never作为非允许属性的类型,只要你不写这些属性就不会触发校验,一旦写入就会因为无法给never类型赋值直接报错,刚好匹配你的需求。
内容的提问来源于stack exchange,提问作者sam256
相关产品推荐
相关产品推荐

