快速入门数据校验工具 —— Zod

4 阅读4分钟

什么是 Zod

Zod:github

是一个 TypeScript 优先的数据格式声明和验证库。用过其特有的语法来声明数据的类型。

使用 Zod 只需要声明一次,就可以由 Zod 自动推断出 TS 的类型,并且拥有该类型的动态验证方法。

与 TS 静态校验数据格式不同,Zod 可以动态对数据进行校验,比如在前后端交互中,对于接口获取数据的校验或者在前端对表单进行校验就需要 Zod 这一类数据动态校验的工具库。

在 Zod 中,一般用 schema 来表示 Zod 所定义的数据类型。

安装

npm install zod
deno add npm:zod # deno
yarn add zod # yarn
bun add zod # bun
pnpm add zod # pnpm

使用

声明基本类型

为方便 TS 开发者学习 Zod,这里列一下 TS 和 Zod 的类型对应表

基础数据

import { z } from 'zod'

// string
z.string()

// number
z.number()

// bigint
z.bigint()

// boolean
z.boolean()

// Date
z.date()

// symbol
z.symbol()

// undefiend
z.undefined()

// null
z.null()

// void
z.void()

// any
z.any()

// unknown
z.unknown()

// never
z.never()

promise

使用z.promise来定义 Promise 类型

const numberPromise = z.promise(z.number());

对象

在 zod 中定义对象类型,可以使用z.object来定义:

比如某个对象的类型是

interface User {
  id: string;
  name: string;
  password: string;
  createTime: number;
}

其对应的 zod 结构为:

import { z } from "zod"

const userSchema = z.object({
  id: z.string(),
  name: z.string(),
  password: z.string(),
  createTime: z.number()
})

数组

可以用z.array来定义数组

比如一个数组的 TS 类型是

type Users = string[]

使用 Zod 定义则为

import { z } from "zod"

const usersSchema = z.array(z.string())

Map & Set

使用 z.map 和 z.set 来定义 Map 和 Set 类型

const stringNumberMap = z.map(z.string(), z.number());

type StringNumberMap = z.infer<typeof stringNumberMap>;
// type StringNumber = Map<string, number>
const numberSet = z.set(z.number());

type NumberSet = z.infer<typeof numberSet>;
// Set<number>

枚举

方法 1:使用z.enum 定义

const FruitEnumSchema = z.enum(['Apple', 'Orange'])

这里要注意必须直接传入,如果不直接传入,Zod 就无法自动推断出枚举的类型

除非使用 as const 帮助 Zod 推断枚举的类型

const FRUIT = ['Apple', 'Orange'] as const
const FruitEnumSchema = z.enum(FRUIT)

想要使用的话,则可以通过 .enum 来访问枚举

FruitEnumSchema.enum.Apple

或者使用 .options 来检索选项,得到一个元组

FruitEnumSchema.options // ['Apple', 'Orange'] 

方法二:使用 z.nativeEnum 来使用现有的枚举类型直接生成 Schema

enum Fruits {
  Apple,
  Banana,
}
const FruitEnumSchema = z.nativeEnum(Fruits);

自定义类

可以使用z.instanceof来检查输入是否是一个类的实例,可以用于验证从第三方库中导出的类。

class Test {
  name: string;
}
const TestSchema = z.instanceof(Test);

函数

使用z.function来定义函数类型,通过链式调用argsreturns来定义函数的参数和返回值

const myFunction = z
  .function()
  .args(z.string(), z.number())
  .returns(z.boolean());

可选

可以用z.optional()使任何 Schema 变为可选

const schema = z.optional(z.string());

或者用.optional()方法使一个现有的 Schema 成为可选的

const schema = z.string().optional();

nullable

与转换为可选类似,可以使用nullable将 Schema 变为可以是 null

const nullableString = z.nullable(z.string());

也可以直接调用.nullable()方法使一个现有的 Schema 成为 null 的类型

const nullableString = z.string().nullable()

nullish

相当于是optionalnullable的组合

联合

可以使用z.union将两个 Schema 联合起来,相当于 ts 中的 |;也可以直接用.or()方法,将一个已存在的 Schema 与另一个联合起来

const stringOrNumber = z.union([z.string(), z.number()]);

const stringOrNumber = z.string().or(z.number());

合并

可以使用z.intersection来合并两个 Schema,或者用.and方法

const Person = z.object({
  name: z.string(),
});

const Employee = z.object({
  role: z.string(),
});

const EmployedPerson = z.intersection(Person, Employee);

// equivalent to:
const EmployedPerson = Person.and(Employee);

针对于对象,Zod 更推荐用.merge来合并

Record

在ts中我们常用 Record<string, any> 来定义类似 { [k: string]: any } 的类型,在 Zod 中可以使用z.record来定义 Schema

const userSchema = z.object({ name: z.string() });
const userStoreSchema = z.record(userSchema);

常用方法

parse

任何 Zod Schema 都可以使用parse方法来检查传入的数据是否符合 Schema 的定义。如果不符合,则会抛出错误

const stringSchema = z.string();
stringSchema.parse("fish"); // => returns "fish"
stringSchema.parse(12); // throws Error('Non-string type: number');

safeParse

用法作用同 parse 方法,但是在不符合数据定义的情况下不会抛出错误

stringSchema.safeParse(12);
// => { success: false; error: ZodError }

stringSchema.safeParse("billie");
// => { success: true; data: 'billie' }

refine

除了基本的数据格式,还可以通过 refine 来自定义数据校验的规则。

const myString = z.string().refine((val) => val.length <= 255, { 
  message: "String can't be more than 255 characters",
})

也可以在 refine 中设置异步的校验规则,但这样的话,必须使用.parseAsync方法来解析数据,否则 Zod 会抛出一个错误

const stringSchema = z.string().refine(async (val) => val.length > 20);
const value = await stringSchema.parseAsync("hello"); // => hello

除了自定义规则,z.numberz.stringz.array等都提供了很多自带的验证功能,比如数字的大小,数组的大小限制等等

获取TS类型

可以使用z.infer<typeof mySchema>来获取 Schema 所定义的 TS 类型。

const A = z.string();
type A = z.infer<typeof A>; // string