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

为何返回never的抛出函数作为对象属性时无法实现类型收窄?

TypeScript:对象属性的never返回函数无法收窄类型的问题

先看具体场景:

首先定义一个返回never的抛出函数,以及一个可空字符串:

function throwMyError(): never {
    throw new Error("My Custom error")
}

declare const str: string | null

直接调用这个函数时,TypeScript能正常将str收窄为非空类型:

if(!str) throwMyError()
str.toLocaleLowerCase() // 无报错

但如果把这个函数作为对象属性调用,就无法实现类型收窄:

const foo = { throwMyError }
if (!str) foo.throwMyError()
str.toLocaleLowerCase() // 'obj' is possibly 'null'.(18047)

或者直接在对象里定义never返回的函数,结果一样:

const bar = {
    throwMyError: (): never => {
        throw new Error()
    }
}
if (!str) bar.throwMyError()
str.toLocaleLowerCase() // 'obj' is possibly 'null'.(18047)

为什么会出现这个问题?

TypeScript的控制流分析不会追踪对象属性的类型稳定性。

当你直接调用throwMyError()时,TS能明确识别这个函数的返回类型是never,知道它会终止当前代码分支,所以会自动把后续代码里的str收窄为string。

但函数作为对象属性存在时,TS默认认为对象属性是可变的——哪怕你用const声明对象,TS也无法保证后续没有代码修改属性的指向(比如在其他地方给foo.throwMyError重新赋值)。因此TS不会假设foo.throwMyError()一定会返回never并终止分支,自然不会对str做类型收窄。


不使用类型谓词的解决方案

方案1:用as const断言对象为只读

通过as const告诉TS对象的属性是不可变的,这样TS就能确定属性函数的类型始终是() => never:

const foo = { throwMyError } as const
if (!str) foo.throwMyError()
str.toLocaleLowerCase() // 无报错

方案2:把对象属性函数赋值给局部变量

先把对象里的函数提取到局部变量,TS能识别局部变量的类型稳定性,从而正常收窄:

const foo = { throwMyError }
const throwErr = foo.throwMyError
if (!str) throwErr()
str.toLocaleLowerCase() // 无报错

方案3:在条件分支内直接定义抛出逻辑

如果不需要复用对象,也可以直接在if分支里写抛出逻辑,或者用立即执行函数:

if (!str) {
    ((): never => { throw new Error() })()
}
str.toLocaleLowerCase() // 无报错

内容的提问来源于stack exchange,提问作者Alex Wayne

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 04:15:34