TypeScript如何定义仅允许小写字母的字符串字面量类型
现有实现的问题
- 递归逻辑错误:你定义的
LowerString终止分支返回的是单个小写字母联合类型${Lower},无法适配不同长度的输入字符串;同时递归拼接时直接用了任意小写字母而非原字符串的字符,导致最终类型会被推导为所有可能的小写字母组合的联合,无法起到校验传入字符串是否全小写的作用。 - 泛型使用不符合预期:你将
LowerString<string>作为泛型约束传入时,TS无法对宽泛的string类型做细粒度的字面量校验,同时额外定义ID泛型参数也增加了使用成本。
另外补充说明:TypeScript目前的类型系统不支持对宽泛的string类型做默认的小写约束,必须在字面量推导的场景下才能触发校验,不需要额外加泛型的实现可以参考以下方案。
修正方案
方案1:基于自定义小写字母枚举的实现
修正后的LowerString类型可以正确校验字符串是否全为小写字母:
type Lower = | "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"; type LowerString<T extends string> = T extends `${infer First}${infer Rest}` ? First extends Lower ? `${First}${LowerString<Rest>}` : never : T;
不需要给Resource加泛型的写法,配合TS 4.9+的satisfies操作符使用即可:
type Resource = { id: string; }; // 合法示例,正常通过校验 const validResource = { id: "user123" } satisfies Resource & { id: LowerString<typeof validResource.id> }; // 非法示例,包含大写字母会触发编译报错 const invalidResource = { id: "User123" } satisfies Resource & { id: LowerString<typeof invalidResource.id> };
方案2:基于TS内置工具类型的简化实现
TS本身提供了内置的Lowercase工具类型,可以直接用来判断字符串是否全小写,不需要手动枚举所有小写字母:
type LowerString<T extends string> = T extends Lowercase<T> ? T : never; // 基于标签类型实现无额外语法的校验 type LowercaseString = string & { __tag: 'lowercase' }; function defineLowerId<T extends string>(id: LowerString<T>): LowercaseString { return id as LowercaseString; } type Resource = { id: LowercaseString; }; // 合法示例 const res: Resource = { id: defineLowerId("order2024") }; // 非法示例,包含大写字母编译报错 const invalidRes: Resource = { id: defineLowerId("Order2024") };
内容的提问来源于stack exchange,提问作者user3612643
相关产品推荐
相关产品推荐

