TypeScript如何实现数字/字符串字面量类型减法与RangeUnion泛型
解决方案
类型级减法实现
TypeScript 本身没有内置的类型级算术运算能力,我们可以通过数组长度匹配的方案实现正整数减法,同时兼容数字、字符串数字两种入参格式:
// 构造指定长度的数组 type BuildArray<N extends number, T extends any[] = []> = T['length'] extends N ? T : BuildArray<N, [...T, any]>; // 数字字面量减法:仅支持正整数,且被减数 >= 减数 type Subtract<A extends number, B extends number> = BuildArray<A> extends [...BuildArray<B>, ...infer Rest] ? Rest['length'] : never; // 字符串数字转数字字面量 type StrToNum<S extends string> = S extends `${infer N extends number}` ? N : never; // 数字字面量转字符串数字 type NumToStr<N extends number> = `${N}`; // 字符串字面量减法 type StrSubtract<A extends string, B extends string> = NumToStr<Subtract<StrToNum<A>, StrToNum<B>>>;
效果验证:
type type4 = Subtract<5, 1> // 类型为 4 type type4Str = StrSubtract<'5', '1'> // 类型为 '4'
RangeUnion 泛型实现
兼容数字、字符串数字入参,返回从 0 到入参值的所有整数字面量(同时返回数字、字符串两种格式,兼容数组索引取值场景):
type RangeUnion<N extends number | string, Acc extends any[] = [], Res = never> = StrToNum<`${N}`> extends infer Num extends number ? Acc['length'] extends Num ? Res | Acc['length'] | NumToStr<Acc['length']> : RangeUnion<N, [...Acc, any], Res | Acc['length'] | NumToStr<Acc['length']>> : never;
效果验证:
// 传入字符串 '1',返回 0|1|'0'|'1',符合需求 type Union01 = RangeUnion<'1'>
业务场景适配
和你现有的工具类型结合后效果完全符合预期:
// 你原有的工具类型 type GetIndex<A extends any[], T> = { [K in keyof A]: A[K] extends T ? K : never; }[number]; type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never; // 实际业务代码测试 type Arr = [{ type0: any }, { type1: any }, { type2: any },] type Type1 = GetIndex<Arr, { type1: any }> // 类型为 '1' type TargetUnion = RangeUnion<Type1> // 0|1|'0'|'1' type DesiredType = UnionToIntersection<Arr[TargetUnion]> // 类型为 { type0: any } & { type1: any }
注意:该方案受 TypeScript 递归深度限制,仅支持长度小于 1000 的数值,完全覆盖常规前端组件开发场景。
内容的提问来源于stack exchange,提问作者Eliav Louski
相关产品推荐
相关产品推荐

