如何在运行时验证TypeScript接口的属性值合法性?
TypeScript 运行时校验对象是否符合接口(含枚举值验证)
我希望在运行时检查对象是否符合指定的TypeScript接口,要求对象的每个属性都拥有允许的值。
比如下面的代码,'nope'不是Switch枚举的有效值,编译时会报错,但我需要的是运行时校验:
enum Switch { 'on', 'off', } interface Foo { State: Switch } const f: Foo = { State: 'nope' }
我原本以为类型谓词是可行方案,但发现它只能判断接口结构,没法验证属性值的有效性。比如下面的例子,传入无效值时校验仍然会通过:
enum Switch { 'on', 'warn', } interface Foo { Bar: number | Switch } const isValid = (foo: any): foo is Foo => { return (foo as Foo) !== undefined } console.log(isValid({ Bar: 'nope' })) // 输出 true,但实际值无效
请问有没有无需逐个遍历属性就能验证这些值的方法?
解决方案
首先明确:TypeScript的类型系统是编译时的,编译后所有类型信息都会被擦除,所以不可能完全不遍历属性/值就实现运行时校验——毕竟运行时根本没有接口的元数据可以直接读取。不过可以通过工具或代码生成来减少手动遍历的工作量:
1. 类型守卫+枚举校验(手动但可靠)
虽然需要遍历属性,但可以把枚举的校验逻辑封装起来,避免重复代码:
enum Switch { 'on', 'warn', } interface Foo { Bar: number | Switch } // 封装枚举的校验函数 const isSwitch = (val: unknown): val is Switch => { return Object.values(Switch).includes(val) } // 针对Foo的类型守卫 const isValidFoo = (foo: any): foo is Foo => { return ( typeof foo === 'object' && foo !== null && (typeof foo.Bar === 'number' || isSwitch(foo.Bar)) ) } console.log(isValidFoo({ Bar: 'nope' })) // 输出 false console.log(isValidFoo({ Bar: Switch.on })) // 输出 true console.log(isValidFoo({ Bar: 123 })) // 输出 true
2. 代码生成工具(自动生成校验逻辑)
如果不想手动写校验函数,可以用工具根据TypeScript接口自动生成运行时校验代码,比如:
- 使用
zod:先定义Zod schema,再导出对应的TypeScript类型,同时获得运行时校验能力import { z } from 'zod' const SwitchSchema = z.enum(['on', 'warn']) const FooSchema = z.object({ Bar: z.union([z.number(), SwitchSchema]) }) // 自动导出TypeScript类型 type Foo = z.infer<typeof FooSchema> // 运行时校验 const result = FooSchema.safeParse({ Bar: 'nope' }) console.log(result.success) // 输出 false ts-pattern或io-ts也能实现类似效果,核心思路都是把类型定义和运行时校验逻辑绑定在一起,避免重复编写。
为什么不能“无需遍历”?
因为编译后的JavaScript里,interface Foo的类型信息完全消失了——枚举会被编译成对象,但接口连对象都不会生成。所以运行时根本没有“接口的属性列表”和“允许的属性值范围”可以直接读取,必须通过某种方式(手动写校验、工具生成校验)把这些信息编码到运行时代码里。
内容的提问来源于stack exchange,提问作者Cjmarkham
相关产品推荐
相关产品推荐

