TypeScript如何实现联合类型参数排他校验 阻止传入交叉属性对象
TypeScript联合类型非法传参拦截方案
问题现象
TypeScript默认采用结构类型(鸭子类型)检查机制,使用普通联合类型Teacher | Student作为参数类型时,无法拦截同时包含两个类型独有字段的非法传参,复现代码如下:
interface Teacher { age: number salary: number } interface Student { age: number classes: string } type Person = Teacher | Student function getAge(person: Person) { return person.age } getAge({age: 34, salary: 1000}) // 校验通过,符合Teacher类型 getAge({age: 16, classes: '5-4'}) // 校验通过,符合Student类型 getAge({age: 20, salary: 1000, classes: '5-4'}) // 预期报错,实际校验通过
出现该现象的核心原因是:普通联合类型仅要求传入值满足联合中任意一个成员的结构要求,结构类型规则允许对象持有目标类型未声明的额外字段;当额外字段恰好属于联合的其他成员时,TS默认不会判定为类型错误。
实现方案
要拦截同时持有多个联合成员独有字段的传参,需要将普通联合改造为互斥联合,有两种常用实现方式:
- 方案1:使用可辨识联合(官方推荐)
为联合的每个类型添加一个值唯一的判别字段,TS可以通过该字段明确区分对象所属的类型,自动拦截字段混用的情况:interface Teacher { role: 'teacher' // 独有判别字段 age: number salary: number } interface Student { role: 'student' // 独有判别字段 age: number classes: string } type Person = Teacher | Student function getAge(person: Person) { return person.age } // 以下非法调用会直接触发类型错误 getAge({role: 'teacher', age: 20, salary: 1000, classes: '5-4'}) - 方案2:无额外字段的互斥类型实现
如果不想新增判别字段,可以通过工具类型结合never类型,强制要求非共有字段如果出现就必须是never类型(即不允许出现),实现字段互斥:// 互斥联合工具类型 type MutuallyExclusive<T, U> = | (T & Partial<Record<Exclude<keyof U, keyof T>, never>>) | (U & Partial<Record<Exclude<keyof T, keyof U>, never>>) interface Teacher { age: number salary: number } interface Student { age: number classes: string } type Person = MutuallyExclusive<Teacher, Student> function getAge(person: Person) { return person.age } // 同时传入salary和classes会触发类型错误 getAge({age: 20, salary: 1000, classes: '5-4'})
内容的提问来源于stack exchange,提问作者KOO
相关产品推荐
相关产品推荐

