TypeScript 和 Zod 都与“类型安全”有关,但它们工作的时间和对象完全不同。
一句话概括:
TypeScript 在编译阶段检查代码,Zod 在运行阶段检查真实数据。
TypeScript 类型会被删除
下面是一段 TypeScript:
function double(value: number): number {
return value * 2;
}转换成 JavaScript 后,类型信息会消失:
function double(value) {
return value * 2;
}这叫类型擦除。浏览器执行 JavaScript 时,并不知道 value 曾经被声明为 number。
TypeScript 可以根据代码推断类型,也可以通过联合类型表达多种可能:
type Result =
| { status: "success"; data: string[] }
| { status: "error"; message: string };判断 status 后,TypeScript 可以缩小变量的可能类型:
function describe(result: Result) {
if (result.status === "success") {
return result.data.length;
}
return result.message;
}这个过程叫类型收窄。它能帮助我们安全地读取不同结构中的字段。
as 不会验证数据
开发者有时会这样处理外部数据:
const message = input as Message;但 as Message 的含义只是“请让 TypeScript 相信这份数据符合 Message”。它不会检查字段,也不会转换数据。
编译后,上面的代码基本只剩:
const message = input;如果 content 实际是数字,as Message 不会立刻报错。只有后面将它当成字符串使用时,程序才可能失败:
message.content.toUpperCase();因此,类型断言不能代替数据验证。
Zod 在运行时验证数据
Zod Schema 是真正存在于 JavaScript 运行时的对象:
import { z } from "zod";
const MessageSchema = z.discriminatedUnion("kind", [
z.object({
kind: z.literal("text"),
content: z.string()
}),
z.object({
kind: z.literal("image"),
url: z.string()
})
]);使用 safeParse 可以获得明确的成功或失败结果:
const result = MessageSchema.safeParse(input);
if (result.success) {
console.log(result.data);
} else {
console.log(result.error.issues);
}parse 验证失败时会抛出异常,而 safeParse 会把错误放进返回结果。
Zod 还可以从 Schema 推导 TypeScript 类型:
type Message = z.infer; 这样 Schema 成为运行时数据结构的真实来源,同时 TypeScript 继续保护验证后的业务代码。
正确的数据边界
外部数据不应该一进入程序就被假定为可信数据。更稳妥的流程是:
API / 表单 / localStorage / AI 输出
-> unknown
-> Zod 验证
-> 可信的 TypeScript 数据
-> 业务逻辑TypeScript 负责检查我们如何使用数据,Zod 负责确认收到的数据是否真的符合预期。两者不是竞争关系,而是分别守住编译期和运行时两道边界。

没有回应