指南 · 基础
版本基线 Valibot 1.4.2。本篇把「会装会用」推进到「懂构件与语义」:schema 全景、
pipe()与 action(校验 vs 转换)、可选/可空与默认值、对象额外键策略、类型推导。
速查
- 基线:Valibot
1.4.2,零依赖、ESM / CommonJS 双入口、构建目标 ES2020;TypeScript 最低5.0.2 - 心智模型:schema 定义类型,action 进入 pipeline,method 以 schema 或 action 为首参进行解析、派生或包装
v.pipe(schema, ...items)从左到右执行,schema 后最多再接 19 个 schema / action;更长流程可嵌套 pipe- 字面量集合用
v.picklist([...]);TypeScript enum 对象用v.enum(E),具名导入名为enum_ - 可选包装:
optional只接受undefined、nullable只接受null、nullish同时接受两者 - 对象缺失的 optional 键默认不执行其 pipe;提供默认值后 pipeline 才会运行且输出键变为必有
- 对象额外键:
object剔除、strictObject报错、looseObject保留、objectWithRest校验其余值 - 类型:日常读取
InferOutput;transform、默认值、brand、readonly 等场景再区分InferInput
一、三类构件全景
Valibot 把一切拆成三类函数,记住它们的角色就抓住了全部 API:
| 构件 | 角色 | 代表 | 用法 |
|---|---|---|---|
| schema | 定义数据类型,是 pipeline 起点 | string number object array union | 直接调用或嵌套 |
| action | 校验 / 转换 / 元数据 | email minLength trim transform brand | 只能放进 pipe() |
| method | 以 schema 为首参,使用或改造它 | parse safeParse pick partial fallback | v.method(Schema, ...) |
ts
import * as v from "valibot";
const Schema = v.pipe(
// pipe 是 method
v.string(), // schema:起点
v.trim(), // action:转换
v.email(), // action:校验
);
v.parse(Schema, " a@b.com "); // method:使用二、常用 schema 族
ts
// 基础类型
v.string();
v.number();
v.boolean();
v.bigint();
v.date();
v.symbol();
v.null();
v.undefined();
v.any();
v.unknown();
// 复合类型
v.object({ name: v.string(), age: v.number() }); // 对象
v.array(v.string()); // 数组
v.tuple([v.string(), v.number()]); // 定长元组
v.record(v.string(), v.number()); // 键值记录
v.map(v.string(), v.number()); // Map
v.set(v.number()); // Set
// 特殊
v.literal("admin"); // 字面量
v.picklist(["light", "dark"]); // 字面量列表(≈ Zod z.enum)
v.union([v.string(), v.number()]); // 联合
v.intersect([A, B]); // 交叉
v.optional(v.string()); // 可选
v.nullable(v.string()); // 可空⚠️
v.picklist([...])校验字面量列表(对应 Zod 的z.enum);v.enum(...)校验 TS 原生 enum 对象(源码导出名enum_,因enum是保留字)。两者别混。
三、pipe:校验 vs 转换
pipe() 数据从左到右流过每个 action。action 分两种:
- 校验 action:只检查不改值,不通过就产生 issue。如
emailurlregexminLengthmaxLengthintegerminValuemaxValuestartsWithendsWith。 - 转换 action:改变值(可改类型),把新值传给下一步。如
trimtoNumbertoDatetoUpperCasetoLowerCase。
ts
// 校验:字符串、去空白、是邮箱、以 @qq.com 结尾
const Email = v.pipe(v.string(), v.trim(), v.email(), v.endsWith("@qq.com"));
// 转换:字符串 → 数字
const Num = v.pipe(v.string(), v.toNumber()); // 输入 string,输出 number
// 数值范围(注意用 minValue/maxValue,不是 minLength)
const Age = v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(150));顺序很重要:转换通常放在校验前(如先
trim再
自定义校验与转换
ts
// check:自定义布尔校验(≈ Zod 的 refine)
const Username = v.pipe(
v.string(),
v.check((s) => /^[a-z0-9_]+$/.test(s), "只能用小写字母、数字、下划线"),
);
// transform:自定义转换
const Slug = v.pipe(
v.string(),
v.transform((s) => s.toLowerCase().replace(/\s+/g, "-")),
);四、可选、可空与默认值
ts
v.optional(v.string()); // 额外接受 undefined
v.nullable(v.string()); // 额外接受 null
v.nullish(v.string()); // 额外接受 null 和 undefined
// 第二参 = 默认值(optional 在缺省/undefined 时补齐)
v.optional(v.string(), "默认值");
v.optional(v.date(), () => new Date()); // 也可传工厂函数三者分工要分清:optional 只管 undefined、nullable 只管 null、nullish 两者都管。
⚠️ 缺省陷阱:对象里 optional 键缺失且没给默认值时,该字段的
pipe(包括transform)不会执行,输出类型是可选键;给了默认值后才会执行,且该键变必有。这是高频踩坑点。
五、对象的额外键策略
Valibot 用不同函数表达对额外键的处理(而非 Zod 的 .strict()/.passthrough() 方法链):
| 函数 | 多余键处理 |
|---|---|
v.object({...}) | 默认:剔除/忽略多余键 |
v.strictObject({...}) | 有多余键就报错 |
v.looseObject({...}) | 保留多余键 |
v.objectWithRest(entries, rest) | 给多余键指定 schema |
ts
v.strictObject({ id: v.number() }); // {id:1, extra:2} → 报错
v.looseObject({ id: v.number() }); // {id:1, extra:2} → 保留 extra六、类型推导:InferInput vs InferOutput
ts
const Schema = v.optional(v.string(), "hi");
type In = v.InferInput<typeof Schema>; // string | undefined(校验前)
type Out = v.InferOutput<typeof Schema>; // string(校验后,默认值已补)InferOutput:校验后的输出类型,日常最常用(≈ Zod 的z.infer)。InferInput:校验前的输入类型,少数场景用(如表单原始值类型)。- 二者何时不同:有
transform、有默认值的optional/nullable、brand、readonly时。纯校验(如pipe(string, email))则输入输出一致。
进入 指南 · 进阶:递归与判别联合、跨字段校验、对象工具方法、错误处理与 i18n、异步校验。