为何返回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
相关产品推荐
相关产品推荐

