如何定义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
相关产品推荐
相关产品推荐

