valibot-usage

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Valibot Usage

Valibot 使用指南

This skill helps you work effectively with Valibot, the modular and type-safe schema library for validating structural data.
本内容将帮助你高效使用Valibot——一款用于验证结构化数据的模块化、类型安全的Schema库。

When to use this skill

适用场景

  • When the user asks about schema validation with Valibot
  • When creating or modifying Valibot schemas
  • When parsing or validating user input
  • When the user mentions Valibot, schema, or validation
  • When migrating from Zod to Valibot
  • 当用户询问关于Valibot的Schema验证相关问题时
  • 当创建或修改Valibot Schema时
  • 当解析或验证用户输入时
  • 当用户提及Valibot、Schema或验证时
  • 当从Zod迁移到Valibot时

CRITICAL: Valibot vs Zod — Do Not Confuse!

⚠️ 重要提示:Valibot与Zod的区别——请勿混淆!

Valibot and Zod have different APIs. Never mix them up!
Valibot和Zod的API完全不同,绝不能混用!

Key Differences

核心差异

FeatureZod ❌Valibot ✅
Import
import { z } from 'zod'
import * as v from 'valibot'
ValidationsChained methods:
.email().min(5)
Pipeline:
v.pipe(v.string(), v.email(), v.minLength(5))
Parsing
schema.parse(data)
v.parse(schema, data)
Safe parsing
schema.safeParse(data)
v.safeParse(schema, data)
Optional
z.string().optional()
v.optional(v.string())
Nullable
z.string().nullable()
v.nullable(v.string())
Default
z.string().default('x')
v.optional(v.string(), 'x')
Transform
z.string().transform(fn)
v.pipe(v.string(), v.transform(fn))
Refine/Check
z.string().refine(fn)
v.pipe(v.string(), v.check(fn))
Enum
z.enum(['a', 'b'])
v.picklist(['a', 'b'])
Native enum
z.nativeEnum(MyEnum)
v.enum(MyEnum)
Union
z.union([a, b])
v.union([a, b])
Discriminated union
z.discriminatedUnion('type', [...])
v.variant('type', [...])
Intersection
z.intersection(a, b)
v.intersect([a, b])
Min/max length
.min(5).max(10)
v.minLength(5), v.maxLength(10)
Min/max value
.gte(5).lte(10)
v.minValue(5), v.maxValue(10)
Infer type
z.infer<typeof Schema>
v.InferOutput<typeof Schema>
Infer input
z.input<typeof Schema>
v.InferInput<typeof Schema>
特性Zod 错误写法 ❌Valibot 正确写法 ✅
导入方式
import { z } from 'zod'
import * as v from 'valibot'
链式验证写法
.email().min(5)
管道式:
v.pipe(v.string(), v.email(), v.minLength(5))
解析数据
schema.parse(data)
v.parse(schema, data)
安全解析
schema.safeParse(data)
v.safeParse(schema, data)
可选字段
z.string().optional()
v.optional(v.string())
可空字段
z.string().nullable()
v.nullable(v.string())
默认值
z.string().default('x')
v.optional(v.string(), 'x')
类型转换
z.string().transform(fn)
v.pipe(v.string(), v.transform(fn))
自定义验证
z.string().refine(fn)
v.pipe(v.string(), v.check(fn))
枚举值
z.enum(['a', 'b'])
v.picklist(['a', 'b'])
原生枚举
z.nativeEnum(MyEnum)
v.enum(MyEnum)
联合类型
z.union([a, b])
v.union([a, b])
区分联合类型
z.discriminatedUnion('type', [...])
v.variant('type', [...])
交叉类型
z.intersection(a, b)
v.intersect([a, b])
长度限制
.min(5).max(10)
v.minLength(5), v.maxLength(10)
数值范围
.gte(5).lte(10)
v.minValue(5), v.maxValue(10)
推断输出类型
z.infer<typeof Schema>
v.InferOutput<typeof Schema>
推断输入类型
z.input<typeof Schema>
v.InferInput<typeof Schema>

Common Mistakes to Avoid

需避免的常见错误

typescript
// ❌ WRONG - This is Zod syntax, NOT Valibot!
const Schema = v.string().email().min(5);
const result = Schema.parse(data);

// ✅ CORRECT - Valibot uses functions and pipelines
const Schema = v.pipe(v.string(), v.email(), v.minLength(5));
const result = v.parse(Schema, data);
typescript
// ❌ WRONG - Zod-style optional
const Schema = v.object({
  name: v.string().optional(),
});

// ✅ CORRECT - Valibot wraps with optional()
const Schema = v.object({
  name: v.optional(v.string()),
});
typescript
// ❌ WRONG - Zod-style default
const Schema = v.string().default("hello");

// ✅ CORRECT - Valibot uses second argument
const Schema = v.optional(v.string(), "hello");
typescript
// ❌ 错误写法——这是Zod语法,不是Valibot的!
const Schema = v.string().email().min(5);
const result = Schema.parse(data);

// ✅ 正确写法——Valibot使用函数和管道式写法
const Schema = v.pipe(v.string(), v.email(), v.minLength(5));
const result = v.parse(Schema, data);
typescript
// ❌ 错误写法——Zod风格的可选字段
const Schema = v.object({
  name: v.string().optional(),
});

// ✅ 正确写法——Valibot用optional()包裹
const Schema = v.object({
  name: v.optional(v.string()),
});
typescript
// ❌ 错误写法——Zod风格的默认值
const Schema = v.string().default("hello");

// ✅ 正确写法——Valibot使用第二个参数设置默认值
const Schema = v.optional(v.string(), "hello");

Installation

安装

bash
npm install valibot     # npm
yarn add valibot        # yarn
pnpm add valibot        # pnpm
bun add valibot         # bun
Import with a wildcard (recommended):
typescript
import * as v from "valibot";
Or with individual imports:
typescript
import { object, string, pipe, email, parse } from "valibot";
bash
npm install valibot     # npm
yarn add valibot        # yarn
pnpm add valibot        # pnpm
bun add valibot         # bun
推荐使用通配符导入:
typescript
import * as v from "valibot";
或者使用单独导入:
typescript
import { object, string, pipe, email, parse } from "valibot";

Mental Model

核心概念

Valibot's API is divided into three main concepts:
Valibot的API分为三个核心部分:

1. Schemas

1. Schema定义

Schemas define the expected data type. They are the starting point.
typescript
import * as v from "valibot";

// Primitive schemas
const StringSchema = v.string();
const NumberSchema = v.number();
const BooleanSchema = v.boolean();
const DateSchema = v.date();

// Complex schemas
const ArraySchema = v.array(v.string());
const ObjectSchema = v.object({
  name: v.string(),
  age: v.number(),
});
Schema用于定义预期的数据类型,是所有操作的起点。
typescript
import * as v from "valibot";

// 基础类型Schema
const StringSchema = v.string();
const NumberSchema = v.number();
const BooleanSchema = v.boolean();
const DateSchema = v.date();

// 复杂类型Schema
const ArraySchema = v.array(v.string());
const ObjectSchema = v.object({
  name: v.string(),
  age: v.number(),
});

2. Methods

2. 操作方法

Methods help you use or modify schemas. The schema is always the first argument.
typescript
// Parsing
const result = v.parse(StringSchema, "hello");
const safeResult = v.safeParse(StringSchema, "hello");

// Type guard
if (v.is(StringSchema, data)) {
  // data is typed as string
}
操作方法用于使用或修改Schema,Schema始终作为第一个参数。
typescript
// 解析数据
const result = v.parse(StringSchema, "hello");
const safeResult = v.safeParse(StringSchema, "hello");

// 类型守卫
if (v.is(StringSchema, data)) {
  // 此处data会被推断为string类型
}

3. Actions

3. 验证/转换动作

Actions validate or transform data within a
pipe()
. They MUST be used inside pipelines.
typescript
// Actions are used in pipe()
const EmailSchema = v.pipe(
  v.string(),
  v.trim(),
  v.email(),
  v.endsWith("@example.com"),
);
动作用于在
pipe()
中验证或转换数据,必须在管道中使用。
typescript
// 动作需在pipe()中使用
const EmailSchema = v.pipe(
  v.string(),
  v.trim(),
  v.email(),
  v.endsWith("@example.com"),
);

Pipelines

管道式验证

Pipelines extend schemas with validation and transformation actions. A pipeline always starts with a schema, followed by actions.
typescript
import * as v from "valibot";

const UsernameSchema = v.pipe(
  v.string(),
  v.trim(),
  v.minLength(3, "Username must be at least 3 characters"),
  v.maxLength(20, "Username must be at most 20 characters"),
  v.regex(
    /^[a-z0-9_]+$/i,
    "Username can only contain letters, numbers, and underscores",
  ),
);

const AgeSchema = v.pipe(
  v.number(),
  v.integer("Age must be a whole number"),
  v.minValue(0, "Age cannot be negative"),
  v.maxValue(150, "Age cannot exceed 150"),
);
管道式验证通过验证和转换动作扩展Schema,管道始终以Schema开头,后续跟多个动作。
typescript
import * as v from "valibot";

const UsernameSchema = v.pipe(
  v.string(),
  v.trim(),
  v.minLength(3, "用户名长度至少为3个字符"),
  v.maxLength(20, "用户名长度不能超过20个字符"),
  v.regex(
    /^[a-z0-9_]+$/i,
    "用户名只能包含字母、数字和下划线",
  ),
);

const AgeSchema = v.pipe(
  v.number(),
  v.integer("年龄必须是整数"),
  v.minValue(0, "年龄不能为负数"),
  v.maxValue(150, "年龄不能超过150岁"),
);

Common Validation Actions

常见验证动作

String validations:
  • v.email()
    — Valid email format
  • v.url()
    — Valid URL format
  • v.uuid()
    — Valid UUID format
  • v.regex(pattern)
    — Match regex pattern
  • v.minLength(n)
    — Minimum length
  • v.maxLength(n)
    — Maximum length
  • v.length(n)
    — Exact length
  • v.nonEmpty()
    — Not empty string
  • v.startsWith(str)
    — Starts with string
  • v.endsWith(str)
    — Ends with string
  • v.includes(str)
    — Contains string
Number validations:
  • v.minValue(n)
    — Minimum value (>=)
  • v.maxValue(n)
    — Maximum value (<=)
  • v.gtValue(n)
    — Greater than (>)
  • v.ltValue(n)
    — Less than (<)
  • v.integer()
    — Must be integer
  • v.finite()
    — Must be finite
  • v.safeInteger()
    — Safe integer range
  • v.multipleOf(n)
    — Must be multiple of n
Array validations:
  • v.minLength(n)
    — Minimum items
  • v.maxLength(n)
    — Maximum items
  • v.length(n)
    — Exact item count
  • v.nonEmpty()
    — At least one item
  • v.includes(item)
    — Contains item
  • v.excludes(item)
    — Does not contain item
字符串验证:
  • v.email()
    — 验证邮箱格式
  • v.url()
    — 验证URL格式
  • v.uuid()
    — 验证UUID格式
  • v.regex(pattern)
    — 匹配正则表达式
  • v.minLength(n)
    — 最小长度
  • v.maxLength(n)
    — 最大长度
  • v.length(n)
    — 精确长度
  • v.nonEmpty()
    — 非空字符串
  • v.startsWith(str)
    — 以指定字符串开头
  • v.endsWith(str)
    — 以指定字符串结尾
  • v.includes(str)
    — 包含指定字符串
数值验证:
  • v.minValue(n)
    — 最小值(>=)
  • v.maxValue(n)
    — 最大值(<=)
  • v.gtValue(n)
    — 大于指定值(>)
  • v.ltValue(n)
    — 小于指定值(<)
  • v.integer()
    — 必须是整数
  • v.finite()
    — 必须是有限数
  • v.safeInteger()
    — 安全整数范围
  • v.multipleOf(n)
    — 必须是n的倍数
数组验证:
  • v.minLength(n)
    — 最小元素数量
  • v.maxLength(n)
    — 最大元素数量
  • v.length(n)
    — 精确元素数量
  • v.nonEmpty()
    — 至少包含一个元素
  • v.includes(item)
    — 包含指定元素
  • v.excludes(item)
    — 不包含指定元素

Custom Validation with check()

使用check()自定义验证

typescript
const PasswordSchema = v.pipe(
  v.string(),
  v.minLength(8),
  v.check(
    (input) => /[A-Z]/.test(input),
    "Password must contain an uppercase letter",
  ),
  v.check((input) => /[0-9]/.test(input), "Password must contain a number"),
);
typescript
const PasswordSchema = v.pipe(
  v.string(),
  v.minLength(8),
  v.check(
    (input) => /[A-Z]/.test(input),
    "密码必须包含大写字母",
  ),
  v.check((input) => /[0-9]/.test(input), "密码必须包含数字"),
);

Value Transformations

值转换

These actions modify the value without changing its type:
String transformations:
  • v.trim()
    — Remove leading/trailing whitespace
  • v.trimStart()
    — Remove leading whitespace
  • v.trimEnd()
    — Remove trailing whitespace
  • v.toLowerCase()
    — Convert to lowercase
  • v.toUpperCase()
    — Convert to uppercase
Number transformations:
  • v.toMinValue(n)
    — Clamp to minimum value (if less than n, set to n)
  • v.toMaxValue(n)
    — Clamp to maximum value (if greater than n, set to n)
typescript
const NormalizedEmailSchema = v.pipe(
  v.string(),
  v.trim(),
  v.toLowerCase(),
  v.email(),
);

// Clamp number to range 0-100
const PercentageSchema = v.pipe(v.number(), v.toMinValue(0), v.toMaxValue(100));
以下动作可修改值但不改变其类型:
字符串转换:
  • v.trim()
    — 去除首尾空格
  • v.trimStart()
    — 去除开头空格
  • v.trimEnd()
    — 去除结尾空格
  • v.toLowerCase()
    — 转换为小写
  • v.toUpperCase()
    — 转换为大写
数值转换:
  • v.toMinValue(n)
    — 限制最小值(小于n则设为n)
  • v.toMaxValue(n)
    — 限制最大值(大于n则设为n)
typescript
const NormalizedEmailSchema = v.pipe(
  v.string(),
  v.trim(),
  v.toLowerCase(),
  v.email(),
);

// 将数值限制在0-100范围内
const PercentageSchema = v.pipe(v.number(), v.toMinValue(0), v.toMaxValue(100));

Type Transformations

类型转换

For converting between data types, use these built-in transformation actions:
  • v.toNumber()
    — Convert to number
  • v.toString()
    — Convert to string
  • v.toBoolean()
    — Convert to boolean
  • v.toBigint()
    — Convert to bigint
  • v.toDate()
    — Convert to Date
typescript
// Convert string to number
const PortSchema = v.pipe(v.string(), v.toNumber(), v.integer(), v.minValue(1));

// Convert ISO string to Date
const TimestampSchema = v.pipe(v.string(), v.isoDateTime(), v.toDate());

// Convert to boolean
const FlagSchema = v.pipe(v.string(), v.toBoolean());
如需在数据类型间转换,使用以下内置转换动作:
  • v.toNumber()
    — 转换为数值
  • v.toString()
    — 转换为字符串
  • v.toBoolean()
    — 转换为布尔值
  • v.toBigint()
    — 转换为大整数
  • v.toDate()
    — 转换为Date对象
typescript
// 将字符串转换为数值
const PortSchema = v.pipe(v.string(), v.toNumber(), v.integer(), v.minValue(1));

// 将ISO字符串转换为Date对象
const TimestampSchema = v.pipe(v.string(), v.isoDateTime(), v.toDate());

// 转换为布尔值
const FlagSchema = v.pipe(v.string(), v.toBoolean());

Custom Transformations

使用transform()自定义转换

For custom transformations, use
v.transform()
:
typescript
const DateStringSchema = v.pipe(
  v.string(),
  v.isoDate(),
  v.transform((input) => new Date(input)),
);

// Custom object transformation
const UserSchema = v.pipe(
  v.object({
    firstName: v.string(),
    lastName: v.string(),
  }),
  v.transform((input) => ({
    ...input,
    fullName: `${input.firstName} ${input.lastName}`,
  })),
);
typescript
const DateStringSchema = v.pipe(
  v.string(),
  v.isoDate(),
  v.transform((input) => new Date(input)),
);

// 自定义对象转换
const UserSchema = v.pipe(
  v.object({
    firstName: v.string(),
    lastName: v.string(),
  }),
  v.transform((input) => ({
    ...input,
    fullName: `${input.firstName} ${input.lastName}`,
  })),
);

Object Schemas

对象Schema

Basic Object

基础对象Schema

typescript
const UserSchema = v.object({
  id: v.number(),
  name: v.string(),
  email: v.pipe(v.string(), v.email()),
  age: v.optional(v.number()),
});

type User = v.InferOutput<typeof UserSchema>;
typescript
const UserSchema = v.object({
  id: v.number(),
  name: v.string(),
  email: v.pipe(v.string(), v.email()),
  age: v.optional(v.number()),
});

type User = v.InferOutput<typeof UserSchema>;

Object Variants

对象Schema的变体

typescript
// Regular object - strips unknown keys (default)
const ObjectSchema = v.object({ key: v.string() });

// Loose object - allows and preserves unknown keys
const LooseObjectSchema = v.looseObject({ key: v.string() });

// Strict object - throws on unknown keys
const StrictObjectSchema = v.strictObject({ key: v.string() });

// Object with rest - validates unknown keys against a schema
const ObjectWithRestSchema = v.objectWithRest(
  { key: v.string() },
  v.number(), // unknown keys must be numbers
);
typescript
// 常规对象 - 自动剔除未知字段(默认行为)
const ObjectSchema = v.object({ key: v.string() });

// 宽松对象 - 允许并保留未知字段
const LooseObjectSchema = v.looseObject({ key: v.string() });

// 严格对象 - 存在未知字段时抛出错误
const StrictObjectSchema = v.strictObject({ key: v.string() });

// 带剩余字段的对象 - 未知字段需符合指定Schema
const ObjectWithRestSchema = v.objectWithRest(
  { key: v.string() },
  v.number(), // 未知字段必须是数值类型
);

Optional and Nullable Fields

可选和可空字段

typescript
const ProfileSchema = v.object({
  // Required
  name: v.string(),

  // Optional (can be undefined or missing)
  nickname: v.optional(v.string()),

  // Optional with default
  role: v.optional(v.string(), "user"),

  // Nullable (can be null)
  avatar: v.nullable(v.string()),

  // Nullish (can be null or undefined)
  bio: v.nullish(v.string()),

  // Nullish with default
  theme: v.nullish(v.string(), "light"),
});
typescript
const ProfileSchema = v.object({
  // 必填字段
  name: v.string(),

  // 可选字段(可为undefined或不存在)
  nickname: v.optional(v.string()),

  // 带默认值的可选字段
  role: v.optional(v.string(), "user"),

  // 可空字段(可为null)
  avatar: v.nullable(v.string()),

  // 可为null或undefined的字段
  bio: v.nullish(v.string()),

  // 带默认值的nullish字段
  theme: v.nullish(v.string(), "light"),
});

Object Methods

对象Schema的操作方法

typescript
const BaseSchema = v.object({
  id: v.number(),
  name: v.string(),
  email: v.string(),
  password: v.string(),
});

// Pick specific keys
const PublicUserSchema = v.pick(BaseSchema, ["id", "name"]);

// Omit specific keys
const UserWithoutPasswordSchema = v.omit(BaseSchema, ["password"]);

// Make all optional
const PartialUserSchema = v.partial(BaseSchema);

// Make all required
const RequiredUserSchema = v.required(PartialUserSchema);

// Merge objects
const ExtendedUserSchema = v.object({
  ...BaseSchema.entries,
  createdAt: v.date(),
});
typescript
const BaseSchema = v.object({
  id: v.number(),
  name: v.string(),
  email: v.string(),
  password: v.string(),
});

// 选取指定字段
const PublicUserSchema = v.pick(BaseSchema, ["id", "name"]);

// 剔除指定字段
const UserWithoutPasswordSchema = v.omit(BaseSchema, ["password"]);

// 将所有字段设为可选
const PartialUserSchema = v.partial(BaseSchema);

// 将所有字段设为必填
const RequiredUserSchema = v.required(PartialUserSchema);

// 合并对象Schema
const ExtendedUserSchema = v.object({
  ...BaseSchema.entries,
  createdAt: v.date(),
});

Cross-Field Validation

跨字段验证

typescript
const RegistrationSchema = v.pipe(
  v.object({
    password: v.pipe(v.string(), v.minLength(8)),
    confirmPassword: v.string(),
  }),
  v.forward(
    v.partialCheck(
      [["password"], ["confirmPassword"]],
      (input) => input.password === input.confirmPassword,
      "Passwords do not match",
    ),
    ["confirmPassword"],
  ),
);
typescript
const RegistrationSchema = v.pipe(
  v.object({
    password: v.pipe(v.string(), v.minLength(8)),
    confirmPassword: v.string(),
  }),
  v.forward(
    v.partialCheck(
      [["password"], ["confirmPassword"]],
      (input) => input.password === input.confirmPassword,
      "两次输入的密码不一致",
    ),
    ["confirmPassword"],
  ),
);

Arrays and Tuples

数组和元组

Arrays

数组Schema

typescript
const TagsSchema = v.pipe(
  v.array(v.string()),
  v.minLength(1, "At least one tag required"),
  v.maxLength(10, "Maximum 10 tags allowed"),
);

// Array of objects
const UsersSchema = v.array(
  v.object({
    id: v.number(),
    name: v.string(),
  }),
);
typescript
const TagsSchema = v.pipe(
  v.array(v.string()),
  v.minLength(1, "至少需要一个标签"),
  v.maxLength(10, "最多允许10个标签"),
);

// 对象数组Schema
const UsersSchema = v.array(
  v.object({
    id: v.number(),
    name: v.string(),
  }),
);

Tuples

元组Schema

typescript
// Fixed-length array with specific types
const CoordinatesSchema = v.tuple([v.number(), v.number()]);
// Type: [number, number]

// Tuple with rest
const ArgsSchema = v.tupleWithRest(
  [v.string()], // first arg is string
  v.number(), // rest are numbers
);
// Type: [string, ...number[]]
typescript
// 固定长度数组,每个元素类型指定
const CoordinatesSchema = v.tuple([v.number(), v.number()]);
// 类型:[number, number]

// 带剩余元素的元组
const ArgsSchema = v.tupleWithRest(
  [v.string()], // 第一个参数是字符串
  v.number(), // 剩余参数是数值
);
// 类型:[string, ...number[]]

Unions and Variants

联合类型和变体类型

Union

联合类型

typescript
const StringOrNumberSchema = v.union([v.string(), v.number()]);

const StatusSchema = v.union([
  v.literal("pending"),
  v.literal("active"),
  v.literal("inactive"),
]);
typescript
const StringOrNumberSchema = v.union([v.string(), v.number()]);

const StatusSchema = v.union([
  v.literal("pending"),
  v.literal("active"),
  v.literal("inactive"),
]);

Picklist (for string/number literals)

枚举列表(适用于字符串/数值字面量)

typescript
// Simpler than union of literals
const StatusSchema = v.picklist(["pending", "active", "inactive"]);

const PrioritySchema = v.picklist([1, 2, 3]);
typescript
// 比字面量联合类型更简洁
const StatusSchema = v.picklist(["pending", "active", "inactive"]);

const PrioritySchema = v.picklist([1, 2, 3]);

Variant (discriminated union)

变体类型(区分联合类型)

Use
variant
for better performance with discriminated unions:
typescript
const EventSchema = v.variant("type", [
  v.object({
    type: v.literal("click"),
    x: v.number(),
    y: v.number(),
  }),
  v.object({
    type: v.literal("keypress"),
    key: v.string(),
  }),
  v.object({
    type: v.literal("scroll"),
    direction: v.picklist(["up", "down"]),
  }),
]);
对于区分联合类型,使用
variant
可获得更好的性能:
typescript
const EventSchema = v.variant("type", [
  v.object({
    type: v.literal("click"),
    x: v.number(),
    y: v.number(),
  }),
  v.object({
    type: v.literal("keypress"),
    key: v.string(),
  }),
  v.object({
    type: v.literal("scroll"),
    direction: v.picklist(["up", "down"]),
  }),
]);

Parsing Data

数据解析

parse() — Throws on Error

parse() — 验证失败时抛出错误

typescript
import * as v from "valibot";

const EmailSchema = v.pipe(v.string(), v.email());

try {
  const email = v.parse(EmailSchema, "jane@example.com");
  console.log(email); // 'jane@example.com'
} catch (error) {
  console.error(error); // ValiError
}
typescript
import * as v from "valibot";

const EmailSchema = v.pipe(v.string(), v.email());

try {
  const email = v.parse(EmailSchema, "jane@example.com");
  console.log(email); // 'jane@example.com'
} catch (error) {
  console.error(error); // ValiError 错误对象
}

safeParse() — Returns Result Object

safeParse() — 返回结果对象

typescript
const result = v.safeParse(EmailSchema, input);

if (result.success) {
  console.log(result.output); // Valid data
} else {
  console.log(result.issues); // Array of issues
}
typescript
const result = v.safeParse(EmailSchema, input);

if (result.success) {
  console.log(result.output); // 验证通过的数据
} else {
  console.log(result.issues); // 错误信息数组
}

is() — Type Guard

is() — 类型守卫

typescript
if (v.is(EmailSchema, input)) {
  // input is typed as string
}
typescript
if (v.is(EmailSchema, input)) {
  // 此处input会被推断为string类型
}

Configuration Options

配置选项

typescript
// Abort early - stop at first error
v.parse(Schema, data, { abortEarly: true });

// Abort pipe early - stop pipeline at first error
v.parse(Schema, data, { abortPipeEarly: true });
typescript
// 提前终止验证——遇到第一个错误就停止
v.parse(Schema, data, { abortEarly: true });

// 提前终止管道——管道中遇到第一个错误就停止
v.parse(Schema, data, { abortPipeEarly: true });

Type Inference

类型推断

typescript
import * as v from "valibot";

const UserSchema = v.object({
  name: v.string(),
  age: v.pipe(v.string(), v.transform(Number)),
  role: v.optional(v.string(), "user"),
});

// Output type (after transformations and defaults)
type User = v.InferOutput<typeof UserSchema>;
// { name: string; age: number; role: string }

// Input type (before transformations)
type UserInput = v.InferInput<typeof UserSchema>;
// { name: string; age: string; role?: string | undefined }

// Issue type
type UserIssue = v.InferIssue<typeof UserSchema>;
typescript
import * as v from "valibot";

const UserSchema = v.object({
  name: v.string(),
  age: v.pipe(v.string(), v.transform(Number)),
  role: v.optional(v.string(), "user"),
});

// 输出类型(转换和默认值应用后的类型)
type User = v.InferOutput<typeof UserSchema>;
// { name: string; age: number; role: string }

// 输入类型(转换前的类型)
type UserInput = v.InferInput<typeof UserSchema>;
// { name: string; age: string; role?: string | undefined }

// 错误信息类型
type UserIssue = v.InferIssue<typeof UserSchema>;

Error Handling

错误处理

Custom Error Messages

自定义错误信息

typescript
const LoginSchema = v.object({
  email: v.pipe(
    v.string("Email must be a string"),
    v.nonEmpty("Please enter your email"),
    v.email("Invalid email format"),
  ),
  password: v.pipe(
    v.string("Password must be a string"),
    v.nonEmpty("Please enter your password"),
    v.minLength(8, "Password must be at least 8 characters"),
  ),
});
typescript
const LoginSchema = v.object({
  email: v.pipe(
    v.string("邮箱必须是字符串类型"),
    v.nonEmpty("请输入你的邮箱"),
    v.email("邮箱格式无效"),
  ),
  password: v.pipe(
    v.string("密码必须是字符串类型"),
    v.nonEmpty("请输入你的密码"),
    v.minLength(8, "密码长度至少为8个字符"),
  ),
});

Flattening Errors

扁平化错误信息

typescript
const result = v.safeParse(LoginSchema, data);

if (!result.success) {
  const flat = v.flatten(result.issues);
  // { nested: { email: ['Invalid email format'], password: ['...'] } }
}
typescript
const result = v.safeParse(LoginSchema, data);

if (!result.success) {
  const flat = v.flatten(result.issues);
  // { nested: { email: ['邮箱格式无效'], password: ['...'] } }
}

Issue Structure

错误信息结构

Each issue contains:
  • kind
    : 'schema' | 'validation' | 'transformation'
  • type
    : Function name (e.g., 'string', 'email', 'min_length')
  • input
    : The problematic input
  • expected
    : What was expected
  • received
    : What was received
  • message
    : Human-readable message
  • path
    : Array of path items for nested issues
每个错误信息包含以下字段:
  • kind
    : 'schema' | 'validation' | 'transformation'(错误类型)
  • type
    : 函数名称(如'string', 'email', 'min_length')
  • input
    : 引发错误的输入值
  • expected
    : 预期的内容
  • received
    : 实际接收到的内容
  • message
    : 人类可读的错误提示
  • path
    : 嵌套错误的路径数组

Fallback Values

回退值

typescript
// Static fallback
const NumberSchema = v.fallback(v.number(), 0);
v.parse(NumberSchema, "invalid"); // Returns 0

// Dynamic fallback
const DateSchema = v.fallback(v.date(), () => new Date());
typescript
// 静态回退值
const NumberSchema = v.fallback(v.number(), 0);
v.parse(NumberSchema, "invalid"); // 返回0

// 动态回退值
const DateSchema = v.fallback(v.date(), () => new Date());

Recursive Schemas

递归Schema

typescript
import * as v from "valibot";

type TreeNode = {
  value: string;
  children: TreeNode[];
};

const TreeNodeSchema: v.GenericSchema<TreeNode> = v.object({
  value: v.string(),
  children: v.lazy(() => v.array(TreeNodeSchema)),
});
typescript
import * as v from "valibot";

type TreeNode = {
  value: string;
  children: TreeNode[];
};

const TreeNodeSchema: v.GenericSchema<TreeNode> = v.object({
  value: v.string(),
  children: v.lazy(() => v.array(TreeNodeSchema)),
});

Async Validation

异步验证

For async operations (e.g., database checks), use async variants:
typescript
import * as v from "valibot";

const isUsernameAvailable = async (username: string) => {
  // Check database
  return true;
};

const UsernameSchema = v.pipeAsync(
  v.string(),
  v.minLength(3),
  v.checkAsync(isUsernameAvailable, "Username is already taken"),
);

// Must use parseAsync
const username = await v.parseAsync(UsernameSchema, "john");
对于异步操作(如数据库检查),使用异步版本的API:
typescript
import * as v from "valibot";

const isUsernameAvailable = async (username: string) => {
  // 数据库检查逻辑
  return true;
};

const UsernameSchema = v.pipeAsync(
  v.string(),
  v.minLength(3),
  v.checkAsync(isUsernameAvailable, "该用户名已被占用"),
);

// 必须使用parseAsync进行解析
const username = await v.parseAsync(UsernameSchema, "john");

JSON Schema Conversion

JSON Schema转换

typescript
import { toJsonSchema } from "@valibot/to-json-schema";
import * as v from "valibot";

const EmailSchema = v.pipe(v.string(), v.email());
const jsonSchema = toJsonSchema(EmailSchema);
// { type: 'string', format: 'email' }
typescript
import { toJsonSchema } from "@valibot/to-json-schema";
import * as v from "valibot";

const EmailSchema = v.pipe(v.string(), v.email());
const jsonSchema = toJsonSchema(EmailSchema);
// { type: 'string', format: 'email' }

Naming Conventions

命名规范

Convention 1: Same Name (Recommended for simplicity)

规范1:同名(推荐,简洁明了)

typescript
export const User = v.object({
  name: v.string(),
  email: v.pipe(v.string(), v.email()),
});

export type User = v.InferOutput<typeof User>;

// Usage
const users: User[] = [];
users.push(v.parse(User, data));
typescript
export const User = v.object({
  name: v.string(),
  email: v.pipe(v.string(), v.email()),
});

export type User = v.InferOutput<typeof User>;

// 使用示例
const users: User[] = [];
users.push(v.parse(User, data));

Convention 2: With Suffixes (Recommended when input/output differ)

规范2:带后缀(输入输出类型不同时推荐)

typescript
export const UserSchema = v.object({
  name: v.string(),
  age: v.pipe(v.string(), v.transform(Number)),
});

export type UserInput = v.InferInput<typeof UserSchema>;
export type UserOutput = v.InferOutput<typeof UserSchema>;
typescript
export const UserSchema = v.object({
  name: v.string(),
  age: v.pipe(v.string(), v.transform(Number)),
});

export type UserInput = v.InferInput<typeof UserSchema>;
export type UserOutput = v.InferOutput<typeof UserSchema>;

Common Patterns

常见使用场景

Login Form

登录表单验证

typescript
const LoginSchema = v.object({
  email: v.pipe(
    v.string(),
    v.nonEmpty("Please enter your email"),
    v.email("Invalid email address"),
  ),
  password: v.pipe(
    v.string(),
    v.nonEmpty("Please enter your password"),
    v.minLength(8, "Password must be at least 8 characters"),
  ),
});
typescript
const LoginSchema = v.object({
  email: v.pipe(
    v.string(),
    v.nonEmpty("请输入你的邮箱"),
    v.email("邮箱地址无效"),
  ),
  password: v.pipe(
    v.string(),
    v.nonEmpty("请输入你的密码"),
    v.minLength(8, "密码长度至少为8个字符"),
  ),
});

API Response

API响应验证

typescript
const ApiResponseSchema = v.variant("status", [
  v.object({
    status: v.literal("success"),
    data: v.unknown(),
  }),
  v.object({
    status: v.literal("error"),
    error: v.object({
      code: v.string(),
      message: v.string(),
    }),
  }),
]);
typescript
const ApiResponseSchema = v.variant("status", [
  v.object({
    status: v.literal("success"),
    data: v.unknown(),
  }),
  v.object({
    status: v.literal("error"),
    error: v.object({
      code: v.string(),
      message: v.string(),
    }),
  }),
]);

Environment Variables

环境变量验证

typescript
const EnvSchema = v.object({
  NODE_ENV: v.picklist(["development", "production", "test"]),
  PORT: v.pipe(v.string(), v.transform(Number), v.integer(), v.minValue(1)),
  DATABASE_URL: v.pipe(v.string(), v.url()),
  API_KEY: v.pipe(v.string(), v.minLength(32)),
});

const env = v.parse(EnvSchema, process.env);
typescript
const EnvSchema = v.object({
  NODE_ENV: v.picklist(["development", "production", "test"]),
  PORT: v.pipe(v.string(), v.transform(Number), v.integer(), v.minValue(1)),
  DATABASE_URL: v.pipe(v.string(), v.url()),
  API_KEY: v.pipe(v.string(), v.minLength(32)),
});

const env = v.parse(EnvSchema, process.env);

Date Handling

日期处理

typescript
// String to Date
const DateFromStringSchema = v.pipe(
  v.string(),
  v.isoDate(),
  v.transform((input) => new Date(input)),
);

// Date validation
const FutureDateSchema = v.pipe(
  v.date(),
  v.minValue(new Date(), "Date must be in the future"),
);
typescript
// 字符串转Date对象
const DateFromStringSchema = v.pipe(
  v.string(),
  v.isoDate(),
  v.transform((input) => new Date(input)),
);

// 验证日期是否为未来时间
const FutureDateSchema = v.pipe(
  v.date(),
  v.minValue(new Date(), "日期必须是未来时间"),
);

Additional Resources

额外资源