如何在TypeScript中实现仅编译时的自定义类型校验?
TypeScript编译阶段校验常量值的可行方案
完全可以在TypeScript中实现仅编译阶段的常量值校验,核心是利用TypeScript的条件类型、模板字面量类型和字面量类型约束,在编译时锁定变量的具体值并校验规则,不会产生任何运行时代码。以下是具体实现方案:
一、数值范围校验(例如:数值不得小于5)
通过条件类型判断数值是否符合范围,不符合时返回自定义错误字面量类型,编译时直接抛出对应错误:
// 定义校验类型:数值>=5则返回自身,否则返回错误信息 type Min5<N extends number> = N >= 5 ? N : `错误:数值 ${N} 必须大于等于5`; // 合法常量:编译通过 const validNum: Min5<5> = 5; const validNum2: Min5<12> = 12; // 非法常量:编译报错,提示类型不匹配 const invalidNum: Min5<3> = 3; // 报错信息:Type '3' is not assignable to type '错误:数值 3 必须大于等于5'.
二、字符串格式校验(例如:邮箱格式)
利用模板字面量类型定义合法格式,结合条件类型做校验,不符合时返回自定义错误:
// 定义基础邮箱格式的模板字面量类型 type EmailPattern = `${string}@${string}.${string}`; // 自定义校验类型:符合邮箱格式则返回自身,否则返回错误信息 type ValidateEmail<S extends string> = S extends EmailPattern ? S : `错误:字符串 "${S}" 不符合邮箱格式`; // 合法常量:编译通过 const validEmail: ValidateEmail<'user@example.com'> = 'user@example.com'; // 非法常量:编译报错 const invalidEmail: ValidateEmail<'userexample.com'> = 'userexample.com'; // 报错信息:Type '"userexample.com"' is not assignable to type '错误:字符串 "userexample.com" 不符合邮箱格式'.
三、复杂格式校验(例如:国内手机号)
通过infer关键字和嵌套条件类型实现多规则校验,支持更精细的格式判断:
// 校验国内11位手机号:以1开头,第二位为3/4/5/7/8,全为数字 type ValidatePhone<S extends string> = S extends `${infer D1}${infer D2}${infer Rest}` ? Rest extends `${string}${string}${string}${string}${string}${string}${string}${string}${string}` ? D1 extends '1' ? D2 extends '3' | '4' | '5' | '7' | '8' ? S extends `${number}` ? S : `错误:手机号 "${S}" 必须全为数字` : `错误:手机号 "${S}" 第二位必须是3/4/5/7/8` : `错误:手机号 "${S}" 必须以1开头` : `错误:手机号 "${S}" 长度必须为11位` : `错误:手机号 "${S}" 长度必须为11位`; // 合法常量:编译通过 const validPhone: ValidatePhone<'13912345678'> = '13912345678'; // 非法常量:编译报错 const invalidPhone1: ValidatePhone<'1234567890'> = '1234567890'; // 长度不足 const invalidPhone2: ValidatePhone<'23912345678'> = '23912345678'; // 开头非1
核心原理说明
- 通过指定字面量类型参数,让TypeScript将变量锁定为具体的字面量值,而非宽泛的
number/string类型 - 利用条件类型的分支判断,在编译阶段验证值是否符合规则,不符合时返回包含自定义错误信息的字面量类型,触发编译报错
- 所有校验逻辑仅存在于编译阶段,最终生成的JavaScript代码中不会保留任何校验相关代码,完全满足“仅编译阶段校验”的需求
内容的提问来源于stack exchange,提问作者Krishna Acharya
相关产品推荐
相关产品推荐

