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

如何在编译时限制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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 20:03:19