You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 11:10:23