TypeScript 如何实现匹配含非负整数且后可跟空格的字符串类型
问题分析
你的实现无法匹配末尾带空格的字符串,核心是两个问题:
- TypeScript的模板字面量
infer拆分逻辑不支持基于自定义递归类型的自动拆分:你在Input类型中写的${Integer<infer I>}${Whitespaces<infer W>}属于自定义约束模式,TS类型系统不会主动对输入字符串做回溯拆分来分别满足Integer和Whitespaces的约束,因此无法将"123 "自动拆分为I="123"、W=" "的结构。 - 递归终止分支存在隐患:你的
Digits类型在遇到非数字字符时会返回空字符串,会导致存在非数字字符的字符串错误通过前置数字部分的校验,比如"12a3"会被Digits计算为"12",出现误判。
修复方案
调整类型逻辑,先拆分字符串的全部前缀和后缀,再分别校验前缀为合法非负整数、后缀全为空格即可,既避免联合类型爆炸,也符合TS的类型推导规则:
type NonZeroDigit = "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"; type Digit = "0" | NonZeroDigit; // 校验是否为合法非负整数字符串:不能是0开头的多位数,只能由数字组成 type ValidInt<T extends string> = T extends '0' ? T : T extends `${NonZeroDigit}${infer Rest}` ? Rest extends '' ? T : Rest extends `${Digit}${infer _}` ? ValidInt<Rest> : never : never; // 校验是否为全空格字符串(支持空字符串) type AllSpaces<T extends string> = T extends '' ? T : T extends ` ${infer Rest}` ? AllSpaces<Rest> : never; // 整体校验:拆分整数部分和剩余部分分别校验 type Input<T extends string> = T extends `${infer IntPart}${infer Rest}` ? IntPart extends ValidInt<IntPart> ? Rest extends AllSpaces<Rest> ? T : never : never : never; function test<T extends string>(foo: Input<T>) {}
校验效果
test("0"); // OK test("1234"); // OK test("42 "); // OK test("100 "); // OK test("-9"); // 类型错误 test(""); // 类型错误 test(" "); // 类型错误 test("0123"); // 类型错误 test("who"); // 类型错误 test("123 "); // OK
内容的提问来源于stack exchange,提问作者Phil Kang
相关产品推荐
相关产品推荐

