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

如何定义NonZero非零数字类型及TypeScript泛型Result类型?

解决TypeScript中Result泛型类型及NonZero类型的定义问题

一、实现可生效的NonZero类型

直接使用Exclude<number, 0>无法达到预期的类型收窄效果,因为number是覆盖所有数值的宽泛类型,TypeScript编译器无法区分普通number类型中的0与非零值。我们可以利用数字字面量的分布式条件类型特性来实现精准的NonZero定义:

// 针对字面量数字类型,排除0的NonZero类型
type NonZero<T extends number> = T extends 0 ? never : T;

如果需要一个通用的“非零数字”类型用于约束(避免传入0),可以结合类型断言和辅助函数实现:

// 带标识的非零数字类型,仅用于类型层面区分
type NonZeroNumber = number & { __nonZeroBrand: never };

// 辅助函数:运行时校验非零,类型层面转换为NonZeroNumber
const toNonZero = (n: number): NonZeroNumber => {
  if (n === 0) throw new Error("code不能为0");
  return n as NonZeroNumber;
};

二、优化Result泛型类型定义

你当前的实现需要手动枚举所有非零code,我们可以调整泛型结构,让Result自动根据code是否为0分支,无需手动传入所有可能的code值:

方案1:自动分支的单泛型Result

// 核心Result类型:当code为0时携带自定义数据,非零则携带错误信息
type Result<TData> = 
  | { code: 0; data: TData }
  | { code: NonZeroNumber; reason: string };

// 使用示例
type SomeResult = { some_result: string };

function getSomeResult(): Result<SomeResult> {
  if (Math.random() > 0.5) {
    return { code: 0, data: { some_result: "操作成功" } };
  } else {
    return { code: toNonZero(-1), reason: "权限不足" };
  }
}

const result = getSomeResult();
switch (result.code) {
  case 0:
    console.log(result.data.some_result); // 编译通过,仅能访问data属性
    // result.reason; // 编译报错,符合预期
    break;
  default:
    console.log(result.reason); // 编译通过,仅能访问reason属性
    // result.data; // 编译报错,符合预期
    break;
}

方案2:支持指定错误code的灵活设计

如果需要限定错误code为特定字面量集合,可以拆分成功/失败类型再联合:

// 成功结果:固定code=0,携带自定义数据
type SuccessResult<TData> = { code: 0; data: TData };

// 失败结果:code为指定的非零字面量,携带错误信息
type FailResult<TCode extends number> = { code: NonZero<TCode>; reason: string };

// 联合Result类型
type Result<TData, TErrorCode extends number = number> = SuccessResult<TData> | FailResult<TErrorCode>;

// 使用示例
type SomeResult = { some_result: string };

// 指定错误code为-1、6007、114514
function getSomeResult(): Result<SomeResult, -1 | 6007 | 114514> {
  if (Math.random() > 0.5) {
    return { code: 0, data: { some_result: "操作成功" } };
  } else {
    const errorCodes: (-1 | 6007 | 114514)[] = [-1, 6007, 114514];
    return { 
      code: errorCodes[Math.floor(Math.random() * errorCodes.length)], 
      reason: "操作失败" 
    };
  }
}

const result = getSomeResult();
switch (result.code) {
  case 0:
    console.log(result.data.some_result);
    break;
  case -1:
  case 6007:
  case 114514:
    console.log(result.reason);
    break;
}

关键说明

  • Exclude<number, 0>失效的本质:number是宽泛的顶层类型,TypeScript无法在编译阶段对普通number类型做0与非零的区分,只有当类型是具体数字字面量时,条件类型才能触发分布式分支,实现精准收窄。
  • 上述方案通过字面量类型和联合类型的特性,让编译器能在switch分支中正确推断对应属性的存在性,避免出现不合理的属性访问提示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 13:53:10