数据验证
运行时验证是 farrow-schema 的核心功能之一,让你的应用在运行时也能保持类型安全。
🛡️ 基础验证
typescript
import { Validator } from 'farrow-schema/validator'
class User extends ObjectType {
name = String
age = Number
}
// 验证数据
const result = Validator.validate(User, {
name: 'Alice',
age: 25
})
// 使用 result.kind 进行类型收窄
if (result.kind === 'Ok') {
console.log('验证成功:', result.value)
// result.value 的类型是 { name: string, age: number }
} else {
console.log('验证失败:', result.value.message)
console.log('错误位置:', result.value.path?.join('.'))
}验证选项
严格模式 vs 宽松模式
typescript
// 严格模式(默认):不进行类型转换
const strictResult = Validator.validate(User, {
name: 'Bob',
age: '30' // ❌ 字符串在严格模式下会失败
})
// 宽松模式:尝试类型转换
const lenientResult = Validator.validate(User, {
name: 'Bob',
age: '30' // ✅ 字符串会被转换为数字
}, { strict: false })类型转换规则(宽松模式 strict: false):
"25"→25"true"→true"2024-01-01"→Date
创建专用验证器
在 API 中使用时,推荐创建专用验证器:
typescript
import { createSchemaValidator } from 'farrow-schema/validator'
// 创建验证器(默认严格模式)
const validateUser = createSchemaValidator(User)
// 或者显式指定宽松模式
const validateUserLenient = createSchemaValidator(User, { strict: false })
// 在 API 中使用
app.post('/users', (req, res) => {
const result = validateUser(req.body)
if (result.kind === 'Err') {
return res.status(400).json({
error: result.value.message,
path: result.value.path
})
}
// result.value 已通过类型检查
const user = await createUser(result.value)
res.json(user)
})自定义验证器
使用 ValidatorType
typescript
import { ValidatorType } from 'farrow-schema/validator'
// 邮箱验证器
class EmailType extends ValidatorType<string> {
validate(input: unknown) {
const result = Validator.validate(String, input)
if (result.kind === 'Err') return result
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if (!emailRegex.test(result.value)) {
return this.Err('邮箱格式不正确')
}
return this.Ok(result.value)
}
}
// 使用
class User extends ObjectType {
name = String
email = EmailType // 使用自定义验证器
}参数化验证器
typescript
// 字符串长度验证器
const StringLength = (min: number, max: number) => {
return class StringLength extends ValidatorType<string> {
validate(input: unknown) {
const result = Validator.validate(String, input)
if (result.kind === 'Err') return result
if (result.value.length < min || result.value.length > max) {
return this.Err(`长度必须在 ${min}-${max} 个字符之间`)
}
return this.Ok(result.value)
}
}
}
class Article extends ObjectType {
title = StringLength(5, 100)
content = StringLength(50, 5000)
}正则表达式验证器
typescript
import { RegExp } from 'farrow-schema/validator'
class User extends ObjectType {
username = RegExp(/^[a-zA-Z0-9_]{3,16}$/)
phone = RegExp(/^1[3-9]\d{9}$/)
}错误处理
理解验证结果
typescript
const result = Validator.validate(User, data)
// 推荐:使用 kind 字段进行类型收窄
if (result.kind === 'Err') {
console.log('Error:', result.value.message) // 错误消息
console.log('Path:', result.value.path?.join('.')) // 错误路径,如 "user.address.zipCode"
return
}
// result.value 类型安全
console.log(result.value)
// 也可以使用 isOk/isErr(向后兼容)
if (result.isErr) {
// 处理错误
}实战示例:完整错误处理
typescript
function validateAndCreate(data: unknown): Result<User, string> {
const result = Validator.validate(CreateUserInput, data)
// 利用 kind 进行类型收窄
if (result.kind === 'Err') {
return Err(`Validation failed: ${result.value.message}`)
}
// 业务逻辑验证
if (result.value.age < 18) {
return Err('User must be at least 18 years old')
}
return Ok(createUser(result.value))
}
// 统一处理
const result = validateAndCreate(req.body)
if (result.kind === 'Err') {
return res.status(400).json({ error: result.value })
}
res.json(result.value)