如何校验变量赋值的对象类型?TypeScript类型校验疑问
你遇到的这个问题其实是TypeScript初学者很容易踩的坑——静态类型检查在运行时完全不生效,尤其是当你用JSON.parse这种会绕开TS类型系统的API时,很容易出现“编译时类型合法,但运行时数据不符合要求”的情况。
先解释下为什么你的最后一行代码没报错:JSON.parse的返回类型是any,TypeScript允许any类型赋值给任何其他类型,相当于直接跳过了类型校验。所以哪怕解析后的bar是字符串,TS也不会在编译阶段给你报错。
接下来解决你的核心问题:如何确保运行时数据符合类型要求?这里有几种方案,你可以根据场景选择:
1. 手动编写类型守卫函数(轻量场景首选)
如果你的接口字段不多,自己写一个类型守卫函数是最直接的方式,它既能在运行时校验数据,又能让TypeScript在编译时认可校验后的类型:
interface FooBar { foo?: string; bar?: number; } // 类型守卫函数,返回true时,TS会认为obj是FooBar类型 function isFooBar(obj: unknown): obj is FooBar { // 首先判断是不是对象(排除null,因为typeof null是'object') if (typeof obj !== 'object' || obj === null) { return false; } // 校验可选属性foo:如果存在,必须是string类型 if ('foo' in obj && typeof (obj as { foo?: unknown }).foo !== 'string') { return false; } // 校验可选属性bar:如果存在,必须是number类型 if ('bar' in obj && typeof (obj as { bar?: unknown }).bar !== 'number') { return false; } return true; } // 使用示例 const parsedB = JSON.parse(JSON.stringify(b)); if (isFooBar(parsedB)) { const d: FooBar = parsedB; // 这里TS会完全认可类型 } else { console.error('数据不符合FooBar接口要求,bar应该是number类型!'); }
这种方式的优点是完全可控,不需要依赖第三方库,适合简单的接口校验。
2. 使用第三方校验库(复杂场景高效方案)
如果你的项目中有很多复杂接口,手动写类型守卫会非常繁琐,这时候可以用像Zod或io-ts这类库——它们能让你定义一份“校验规则”,同时自动推导出TypeScript类型,做到“一次定义,静态类型+运行时校验双保障”。
以Zod为例:
import { z } from 'zod'; // 定义Zod校验规则 const FooBarSchema = z.object({ foo: z.string().optional(), // 可选的字符串类型 bar: z.number().optional(), // 可选的数字类型 }); // 从Schema自动推导TS类型 type FooBar = z.infer<typeof FooBarSchema>; // 运行时校验 const parsedB = JSON.parse(JSON.stringify(b)); const validationResult = FooBarSchema.safeParse(parsedB); if (validationResult.success) { const d: FooBar = validationResult.data; // 类型安全 } else { // 详细的错误信息,方便调试 console.error('数据校验失败:', validationResult.error.issues); }
这种方式的好处是避免重复劳动,不用同时写接口和校验逻辑,而且校验错误信息非常详细,适合中大型项目。
为什么必须做运行时校验?
TypeScript的静态类型检查只在编译阶段有效,编译后所有类型信息都会被擦除。而JSON.parse、外部接口返回的数据、本地存储的数据都是运行时动态数据,TS在编译阶段根本无法预知它们的实际类型——所以哪怕你给变量加了类型注解,也只是告诉TS“我认为这个变量是这个类型”,但实际运行时的数据可能完全不符合。
所以无论用哪种方式,运行时校验都是必须的,静态类型只能帮你在编译阶段避免一些低级错误,但覆盖不了动态数据的场景。
内容的提问来源于stack exchange,提问作者devvvmannn

