TypeScript扩展运算符与类型收窄的兼容性问题咨询
TypeScript类型守卫与扩展运算符的类型推断问题
问题代码与现象
原代码中,在if (toto.lastName)类型守卫块内使用扩展运算符将toto赋值给TotoInt2类型变量时,出现类型错误:
interface TotoInt { name: string; lastName: string | null; } interface TotoInt2 { name: string; lastName: string; } const toto: TotoInt = { name: 'toto', lastName: Math.random() < 0.5 ? null : "abc", }; if (toto.lastName) { // 报错:Type 'string | null' is not assignable to type 'string' const toto2: TotoInt2 = { ...toto, }; }
但手动重构对象并使用非空断言!时,TypeScript无报错(此写法适用于类型守卫块外场景):
const toto2: TotoInt2 = { name: toto.name, lastName: toto.lastName!, };
使用版本:TypeScript 4.8.3
原因分析
这是TypeScript对扩展运算符的类型推断限制:
if (toto.lastName)类型守卫会将原对象toto的类型收窄为TotoInt & { lastName: string },但这个收窄仅作用于原对象本身。- 扩展运算符
...toto是创建新对象,TypeScript不会把原对象的类型守卫信息传递到新对象的属性上。新对象的类型仍会被推断为{ name: string; lastName: string | null },无法匹配TotoInt2要求的lastName: string。
无需完整重构的解决方法
方法1:自定义类型守卫收窄对象类型
通过自定义类型守卫函数,直接将toto的类型收窄为TotoInt2,此时扩展运算符可正确推断类型:
function isTotoInt2(toto: TotoInt): toto is TotoInt2 { return toto.lastName !== null; } if (isTotoInt2(toto)) { const toto2: TotoInt2 = { ...toto }; // 无报错 }
方法2:显式覆盖收窄后的属性
在类型守卫块内,仅显式覆盖lastName属性(此时已被收窄为string),其余属性用扩展运算符保留:
if (toto.lastName) { const toto2: TotoInt2 = { ...toto, lastName: toto.lastName // 类型自动匹配string }; }
方法3:类型断言(需确保类型安全)
在确定类型守卫已保证lastName非空的前提下,直接对扩展后的对象做类型断言:
if (toto.lastName) { const toto2: TotoInt2 = { ...toto } as TotoInt2; }
内容的提问来源于stack exchange,提问作者Tan-Aki
相关产品推荐
相关产品推荐

