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

TypeScript实现Rust Result枚举时,私有成员类型断言报错求解

用TypeScript实现Rust风格Result枚举的类型推断解决方案

我正在用TypeScript类实现Rust的Result枚举简易版本,代码如下:

class Result<T, E> {
  private value: T | E;
  private _ok: boolean;

  private constructor(f: () => T) {
    try {
      this.value = f();
      this._ok = true;
    } catch (e) {
      this.value = e as E;
      this._ok = false;
    }
  }

  static from<T, E>(f: () => T) {
    return new Result<T, E>(f);
  }

  is_ok(): this['value'] is T {
    return this._ok;
  }

  unwrap(): T {
    if (this.is_ok()) return this.value as T;
    throw new Error("some error");
  }
}

需求是:当is_ok()返回true时,断言value类型为T;返回false时,value类型为E。但直接断言私有成员value的类型会报错,强制类型转换又无法解决外部用户的类型推断问题,需要更优方案。


最优方案:基于子类实现可辨识联合类型

核心思路是将Result设计为抽象基类,通过OkResult和ErrResult两个子类分别承载成功/失败状态,利用TypeScript的类型谓词自动完成类型缩小:

abstract class Result<T, E> {
  // 类型谓词:断言当前实例为成功状态的子类
  is_ok(): this is OkResult<T, E> {
    return this instanceof OkResult;
  }

  // 类型谓词:断言当前实例为失败状态的子类
  is_err(): this is ErrResult<T, E> {
    return this instanceof ErrResult;
  }

  unwrap(): T {
    if (this.is_ok()) {
      return this.value; // 自动推断为T类型,无需强制转换
    }
    throw new Error(`Unwrap Err failed: ${this.value}`);
  }

  unwrap_err(): E {
    if (this.is_err()) {
      return this.value; // 自动推断为E类型
    }
    throw new Error(`Unwrap Ok failed: ${this.value}`);
  }

  // 静态工厂方法,统一创建Result实例
  static from<T, E>(f: () => T): Result<T, E> {
    try {
      return new OkResult(f());
    } catch (e) {
      return new ErrResult(e as E);
    }
  }
}

// 成功状态子类,公开value属性
class OkResult<T, E> extends Result<T, E> {
  constructor(public value: T) {
    super();
  }
}

// 失败状态子类,公开value属性
class ErrResult<T, E> extends Result<T, E> {
  constructor(public value: E) {
    super();
  }
}

方案优势

  1. 自动类型推断:外部用户调用is_ok()/is_err()后,TypeScript会自动缩小实例类型,无需手动类型转换:
    const res = Result.from<string, Error>(() => {
      return Math.random() > 0.5 ? "success" : (() => { throw new Error("fail") })();
    });
    
    if (res.is_ok()) {
      // res.value自动推断为string
      console.log(res.value.toUpperCase());
    } else {
      // res.value自动推断为Error
      console.log(res.value.message);
    }
    
  2. 符合Rust设计思想:通过子类区分状态,和Rust的Result枚举语义一致,API更直观。
  3. 可扩展性强:后续可以轻松添加map、map_err等Rust Result的核心方法。

备选方案:不拆分子类的类型断言实现

如果不想拆分子类,可以通过定义接口描述不同状态的Result,结合类型谓词完成类型缩小:

class Result<T, E> {
  private value: T | E;
  private readonly _ok: boolean;

  private constructor(f: () => T) {
    try {
      this.value = f();
      this._ok = true;
    } catch (e) {
      this.value = e as E;
      this._ok = false;
    }
  }

  static from<T, E>(f: () => T) {
    return new Result<T, E>(f);
  }

  // 定义成功状态的接口类型
  is_ok(): this is Result<T, E> & { getValue(): T } {
    return this._ok;
  }

  // 定义失败状态的接口类型
  is_err(): this is Result<T, E> & { getValue(): E } {
    return !this._ok;
  }

  // 提供公共方法获取值,避免直接访问私有成员
  getValue(): T | E {
    return this.value;
  }

  unwrap(): T {
    if (this.is_ok()) {
      return this.getValue(); // 自动推断为T类型
    }
    throw new Error("Cannot unwrap Err result");
  }
}

这种方案无需拆分子类,但需要额外的getValue方法,类型谓词的写法也更繁琐,适合对类结构有严格要求的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 18:45:19