为何严格模式下TypeScript有时允许联合类型混合字段?
TypeScript联合类型的“超集匹配”陷阱:实例不匹配任意成员却通过类型检查的场景
TypeScript采用结构子类型规则进行类型校验,这会导致一种容易踩坑的场景:一个对象实例既不完全符合联合类型中的任何单个成员,却依然能通过联合类型的类型检查,进而引发运行时错误。
示例类型定义
type Bar = { bar: true bar2: number } type Baz = { baz: number } type Foo = Bar | Baz
两种赋值场景的差异
- 当直接赋值给
Baz类型时,TypeScript会严格校验属性,额外的bar属性会触发错误:
// 类型错误:对象文字可以只指定已知属性,“bar”不在类型“Baz”中 const bazArg: Baz = {baz: 5, bar: true}
- 但赋值给联合类型
Foo时,不会触发任何类型错误:
// 无类型错误,但该对象既不符合Bar(缺少必填属性bar2),也不符合Baz(多了bar属性) const bazArg: Foo = {baz: 5, bar: true}
由此引发的运行时bug
这种“不完整匹配”的对象会破坏基于属性检查的类型窄化逻辑:
const fn = (foo: Foo): number => 'bar' in foo ? foo.bar2 : foo.baz const bazArg: Foo = {baz: 5, bar: true} console.log(fn(bazArg)) // 输出undefined
这里的问题是:代码通过'bar' in foo判断对象为Bar类型,进而访问bar2属性,但实际对象并没有bar2,最终返回undefined,违反了函数声明的number返回类型约定。
问题根源
TypeScript对联合类型的校验是**“最小满足”**而非“严格匹配”:只要对象满足联合类型中某一个成员的所有必填属性,就会被判定为合法,哪怕它额外拥有其他成员的属性,或者不满足其他成员的要求。在上述例子中,{baz:5, bar:true}满足Baz的必填属性baz,因此被视为符合Foo类型,但它同时携带的bar属性干扰了类型窄化逻辑。
解决方案
- 添加唯一类型标签(推荐)
给联合类型的每个成员添加专属的区分属性,让TypeScript能准确识别并窄化类型:
type Bar = { type: 'bar' // 唯一标签 bar: true bar2: number } type Baz = { type: 'baz' // 唯一标签 baz: number } type Foo = Bar | Baz const fn = (foo: Foo): number => foo.type === 'bar' ? foo.bar2 : foo.baz const validBaz: Foo = {type: 'baz', baz: 5, bar: true} // 无错误,fn会正确返回baz的值 const invalidArg: Foo = {baz:5, bar:true} // 现在会报错:缺少type属性
- 自定义严格类型守卫
通过自定义类型守卫函数,确保对象完全匹配联合类型中的某一个成员:
function isBar(foo: any): foo is Bar { return foo.bar === true && typeof foo.bar2 === 'number' } function isBaz(foo: any): foo is Baz { return typeof foo.baz === 'number' && !(foo.bar) } const fn = (foo: Foo): number => { if (isBar(foo)) return foo.bar2 if (isBaz(foo)) return foo.baz throw new Error('传入的Foo类型不合法') }
内容的提问来源于stack exchange,提问作者Steven Noble
相关产品推荐
相关产品推荐

