Type Safety at Runtime with Zod

使用 Zod 于运行时检验类型

前言

Zod 是以 TypeScript 为首的 Schema 声明和验证库

不管是在 Astro🔗tRPC🔗React Hook Form🔗VeeValidate🔗 背后都有使用到 Zod,这是个简单但充满威力的库值得去了解。

为什么你需要 Zod?

通常为了验证数据的正确性,我们会使用 JSDoc🔗 或是 TypeScript🔗 用于标注类型,虽然 TypeScript 可以帮助我们检查程序执行之前的类型,但在编译成 JavaScript 后还是仍有可能会于运行时因为数据与预期不符而出错,通常源自外部的数据(表单、网址、LocalStorage、API 响应)。这时候就是使用 Zod🔗 的时间,协助在程序执行时检查数据的类型,以达到运行时的数据 Schema 检查。

示例:第三方 API 数据检测

举例来说一个 getPerson 函数会通过 ID 来获取不同人物的数据,就算可以替这个函数返回标注类型,在实际运作时仍就无法肯定 API 响应如同 TypeScript 内的 Person 类型:

interface Person = {
name: string
};
const getPerson = async (id: string): Person => {
return await fetch('https://www.example.com/people/' + id + '.json')
.then((res) => res.json());
};

在使用 Zod 后,我们可以动态的检查 API 响应的数据是否符合 Person 类型,这么做不管在 TypeScript 还是运行时都能确保数据的正确性:

import { z } from 'zod';
const Person = z.object({
name: z.string(),
});
// TS 类型: const getPerson: (id: string) => Promise<string>
const getPerson = async (id: string) => {
const response = await fetch('https://www.example.com/people/' + id + '.json').then((res) => res.json());
const result = Person.parse(response);
// 经过 Zod 验证,确保数据符合 Person
return result;
};
try {
getPerson(1);
} catch (e) {
console.error(e);
}

示例:URL 参数验证

近期使用 Nuxt 框架有留意到这篇文章:Zod and Query String Variables in Nuxt🔗,于是尝试在 Middleware(导航到特定页面前会执行的代码)安插 Zod 来检验用户传入的 URL query 参数正确性,优雅且简洁的解决了验证用户输入的问题!

// 用户要前往的页面 query 必须存在 order 且 filter 可选,否则跳转默认路径 (query: { order: 'DESC' })
import { z } from 'zod';
definePageMeta({
middleware: defineNuxtRouteMiddleware((to) => {
const querySchema = z
.object({
order: z.enum(['ASC', 'DESC']),
filter: z.enum(['CURRENT', 'PAST', 'CURRENT,PAST']).optional(),
})
.strict();
const defaultQuery = { order: 'DESC' };
const targetPath = to.fullPath;
try {
querySchema.parse(to.query);
} catch (e) {
// console.error(e)
return navigateTo({ path: targetPath, query: defaultQuery });
}
}),
});

总结

从以上的范例可以观察到 zod,Zod 有许多验证方法可以使用,例如 z.string()z.number()z.boolean() ……等,这些方法可以组合使用来检查资料是否符合预期的类型,也可以自由定义错误时抛出的讯息,更多可以参考 Zod 文件即可🔗,可以到延伸阅读了解更多实际案例与习题。

延伸阅读