如何在编译时限制isErr函数仅接受未收窄的类型?
限制
isErr仅接受可能为错误类型的输入 问题背景
我参考文章实现了类似Go/fp-ts的返回错误模式,通过TypeScript类型定义在编译时识别函数返回的错误类型。核心代码如下:
const ERR = Symbol('ERR'); type Err<ErrType extends string> = { [ERR]: true; message: string; type: ErrType; }; function isErr<ErrType extends string, Ok>( x: Ok | Err<ErrType>, ): x is Err<ErrType> { return typeof x === 'object' && x != null && ERR in x; } function Err<ErrType extends string>( message: string, type: ErrType, ): Err<ErrType> { return { [ERR]: true, message: message, type: type }; }
使用时能正确推断返回类型,但存在缺陷:当变量被收窄为确定非Err类型时(比如const a = true),调用isErr(a)仍能通过编译,导致不可达代码无法被编译器识别:
function doSomething2() { const a = true if (isErr(a)) { // 此处应触发编译错误,但原代码无提示 console.log("error: " + a.message) return a.type } console.log(a) return "no error" }
解决方案
修改isErr的类型定义,通过条件类型限制参数仅能传入可能包含Err类型的联合类型。当输入类型确定不可能是Err时,参数类型变为never,触发编译错误:
const ERR = Symbol('ERR'); type Err<ErrType extends string> = { [ERR]: true; message: string; type: ErrType; }; // 修改后的isErr类型收窄函数 function isErr<T>( x: T extends Err<any> ? T : (T & Err<any>) extends never ? never : T ): x is Extract<T, Err<any>> { return typeof x === 'object' && x != null && ERR in x; } function Err<ErrType extends string>( message: string, type: ErrType, ): Err<ErrType> { return { [ERR]: true, message: message, type: type }; }
效果验证
- 正常场景仍能正确工作:
function errIfFalse(input: boolean) { if (input) { return Err("input is false", "falseInput") } return "success" } function doSomething() { const a = errIfFalse(true) if (isErr(a)) { console.log("error: " + a.message) return a.type // 类型被正确收窄为"falseInput" } console.log(a) // 类型被正确收窄为"success" return "no error" }
- 无效场景触发编译错误:
function doSomething2() { const a = true if (isErr(a)) { // ❌ 编译器报错:类型“boolean”的参数不能赋给类型“never”的参数 console.log("error: " + a.message) return a.type } console.log(a) return "no error" }
原理说明
- 利用
T extends Err<any>判断输入类型本身就是Err,直接允许传入; - 用
(T & Err<any>) extends never判断输入类型与Err完全无交集,此时将参数类型设为never,阻止调用; - 剩余情况为输入是包含
Err的联合类型,允许传入并通过Extract<T, Err<any>>准确收窄错误类型。
内容的提问来源于stack exchange,提问作者dude
相关产品推荐
相关产品推荐

