farrow-schema 完整 API 参考
目录
核心概念
设计哲学
farrow-schema 是一个类型安全的运行时验证与序列化系统,核心设计理念:
- 类型即文档 - Schema 定义同时作为 TypeScript 类型和运行时验证规则
- 声明式建模 - 使用声明式语法定义复杂数据结构
- 三位一体 - 类型定义、运行时验证、元数据提取统一在一个系统中
- 递归友好 - 天然支持递归和相互引用的复杂类型
- 可组合性 - Schema 可以自由组合、转换、派生
三大核心系统
Schema (类型定义) → TypeScript 类型
Validator (验证) → 运行时数据校验
Formatter (格式化) → API 文档生成模块架构
farrow-schema/
├── index.ts # 主入口:类型定义系统
├── validator.ts # 验证器入口:运行时验证
└── formatter.ts # 格式化器入口:类型元数据提取完整导出清单
主入口 (farrow-schema)
导入路径: farrow-schema
核心职责: 提供类型定义系统、类型工具、辅助函数和 Result 类型
典型使用:
import {
// Schema 基类
Schema, ObjectType, StructType,
// 基础类型
String, Number, Boolean, Date, ID, Int, Float,
// 复合类型构造函数
List, Optional, Nullable, Record, Tuple,
// 联合与交集
Union, Intersect, Literal,
// 结构化类型构造函数
Struct,
// 修饰符
Strict, NonStrict, ReadOnly, ReadOnlyDeep,
// 特殊类型
Any, Unknown, Never, Json,
// 类型工具
TypeOf, getInstance, getSchemaCtor,
// Result 类型
Result, Ok, Err,
// 辅助函数
field, pickObject, omitObject, pickStruct, omitStruct, partial, required, keyof
} from 'farrow-schema'核心导出分类:
Schema 类 - 类型定义
- 基础类型:
String,Number,Boolean,Date,ID,Int,Float - 特殊类型:
Any,Unknown,Never,Json - 抽象类:
Schema,ObjectType等
- 基础类型:
构造函数 - 类型组合
List(Item)- 列表类型Optional(Item)- 可选类型Nullable(Item)- 可空类型Record(Item)- 键值对类型Tuple(...Items)- 元组类型Union(...Items)- 联合类型Intersect(...Items)- 交集类型Literal(value)- 字面量类型Struct(descriptors)- 结构体类型
类型工具 - 类型提取与判断
TypeOf<T>- 提取 TypeScript 类型getInstance(Ctor)- 获取 Schema 实例getSchemaCtor(Ctor)- 获取 Schema 构造函数isSchemaCtor(input)- 判断是否为 Schema 构造函数
辅助函数 - Schema 操作
field(fieldInfo)- 定义字段元数据pickObject(Ctor, keys)- 选择ObjectType字段omitObject(Ctor, keys)- 排除ObjectType字段pickStruct(Ctor, keys)- 选择StructType字段omitStruct(Ctor, keys)- 排除StructType字段partial(Ctor)- 转为可选required(Ctor)- 转为必填keyof(Ctor)取字段键
Result 类型 - 错误处理
Result<T, E>- 结果类型Ok(value)- 成功结果Err(value)- 错误结果
验证器入口 (farrow-schema/validator)
导入路径: farrow-schema/validator
核心职责: 提供运行时数据验证功能
典型使用:
import { Validator, createSchemaValidator, ValidatorType, RegExp } from 'farrow-schema/validator'
import { ObjectType, String, Number } from 'farrow-schema'
class User extends ObjectType {
name = String
age = Number
}
// 方式 1: 直接验证
const result = Validator.validate(User, { name: 'Alice', age: 25 })
// 方式 2: 创建专用验证器
const validateUser = createSchemaValidator(User)
const result2 = validateUser({ name: 'Bob', age: 30 })
// 方式 3: 自定义验证器
class Email extends ValidatorType<string> {
validate(input: unknown) {
const strResult = Validator.validate(String, input)
if (strResult.kind === 'Err') return strResult
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(strResult.value)) {
return this.Err('Invalid email format')
}
return this.Ok(strResult.value)
}
}核心导出:
Validator- 验证器对象Validator.validate(Ctor, input, options)- 验证数据Validator.impl(Ctor, impl)- 注册验证器实现Validator.get(Ctor)- 获取验证器实现
createSchemaValidator(Ctor, options)- 创建专用验证器ValidatorType<T>- 自定义验证器基类RegExp(regexp)- 正则表达式验证器SchemaErr(message, path)- 创建验证错误- 类型导出:
ValidationResult<T>- 验证结果类型ValidationError- 验证错误类型ValidatorOptions- 验证选项
格式化器入口 (farrow-schema/formatter)
导入路径: farrow-schema/formatter
核心职责: 提供 Schema 元数据提取与序列化功能,用于文档生成、代码生成、跨语言类型共享
典型使用:
import { Formatter, formatSchema, isNamedFormatType } from 'farrow-schema/formatter'
import { ObjectType, String, Number, List } from 'farrow-schema'
class User extends ObjectType {
id = String
name = String
age = Number
tags = List(String)
}
// 方式 1: 格式化 Schema
const { typeId, types } = Formatter.format(User)
// 方式 2: 使用别名
const result = formatSchema(User)
// 遍历类型信息
for (const [id, formatType] of Object.entries(types)) {
if (isNamedFormatType(formatType)) {
console.log(`Type ${id}: ${formatType.name}`)
}
}
// 生成文档、客户端代码等
function generateAPIDoc(types: FormatTypes) {
// 根据 types 生成 OpenAPI、GraphQL Schema 等
}核心导出:
Formatter- 格式化器对象Formatter.format(Ctor, context?)- 格式化 SchemaFormatter.formatSchema(Ctor, ctx)- 上下文格式化Formatter.impl(Ctor, impl)- 注册格式化器实现Formatter.get(Ctor)- 获取格式化器实现
formatSchema(Ctor, context?)-Formatter.format的别名isNamedFormatType(formatType)- 判断是否为命名格式类型- 类型导出 (15+ 种格式化类型):
FormatType- 格式化类型联合FormatTypes- 类型字典FormatContext- 格式化上下文FormatScalarType,FormatObjectType,FormatStructType,FormatListType, 等FormatField,FormatFields- 字段格式信息
应用场景:
- API 文档生成 - 自动生成 OpenAPI/Swagger 文档
- 客户端代码生成 - 生成 TypeScript/JavaScript SDK
- GraphQL Schema - 转换为 GraphQL 类型定义
- 跨语言类型共享 - 导出为 JSON Schema、Protocol Buffers 等
- 类型可视化 - 生成类型关系图
模块选择指南
| 需求 | 使用模块 | 典型场景 |
|---|---|---|
| 定义数据结构 | farrow-schema | 定义 API 输入输出类型、业务模型 |
| 验证运行时数据 | farrow-schema/validator | API 参数验证、表单验证 |
| 提取类型元数据 | farrow-schema/formatter | 生成文档、代码生成、跨语言类型共享 |
| 完整功能 | 全部导入 | 完整的类型安全系统 |
完整使用示例:
// 1. 定义 Schema (farrow-schema)
import { ObjectType, String, Number, List } from 'farrow-schema'
class User extends ObjectType {
name = String
age = Number
tags = List(String)
}
// 2. 验证数据 (farrow-schema/validator)
import { Validator } from 'farrow-schema/validator'
const result = Validator.validate(User, {
name: 'Alice',
age: 25,
tags: ['developer', 'typescript']
})
if (result.kind === 'Ok') {
console.log('Valid user:', result.value)
}
// 3. 提取元数据 (farrow-schema/formatter)
import { Formatter } from 'farrow-schema/formatter'
const { typeId, types } = Formatter.format(User)
// 用于生成 API 文档、客户端 SDK 等Schema 类型系统
基础抽象类
Schema
所有 Schema 的基类。
类型签名:
abstract class Schema {
abstract __type: unknown
static displayName?: string
static namespace?: string
static create<T extends SchemaCtor>(this: T, value: TypeOf<T>): TypeOf<T>
}说明:
__type- 用于 TypeScript 类型推导的幽灵属性displayName- Schema 的显示名称(用于格式化)namespace- Schema 的命名空间(用于格式化)create()- 静态工厂方法,创建符合 Schema 的值
使用示例:
class User extends ObjectType {
name = String
age = Number
}
User.displayName = 'UserSchema'
User.namespace = 'com.example'
const user = User.create({ name: 'Alice', age: 25 })基础类型 Schema
Number - 数值类型
class Number extends Schema {
__type!: number
}对应 TypeScript 类型: number
示例:
class Product extends ObjectType {
price = Number
}Int - 整数类型
class Int extends Schema {
__type!: number
}对应 TypeScript 类型: number验证: 验证时会检查是否为整数,非严格模式会向下取整
示例:
class Pagination extends ObjectType {
page = Int
limit = Int
}Float - 浮点数类型
class Float extends Schema {
__type!: number
}对应 TypeScript 类型: number验证: 与 Number 相同
String - 字符串类型
class String extends Schema {
__type!: string
}对应 TypeScript 类型: string
Boolean - 布尔类型
class Boolean extends Schema {
__type!: boolean
}对应 TypeScript 类型: boolean
ID - 标识符类型
class ID extends Schema {
__type!: string
}对应 TypeScript 类型: string验证: 不能为空字符串
示例:
class User extends ObjectType {
id = ID
name = String
}Date - 日期类型
class Date extends Schema {
__type!: DateInstanceType
}对应 TypeScript 类型: Date验证: 接受 Date 实例、时间戳数字、ISO 日期字符串
示例:
class Event extends ObjectType {
title = String
startDate = Date
}复合类型 Schema
List - 列表类型
构造函数:
const List = <T extends SchemaCtorInput>(Item: T): new () => ListType类型:
abstract class ListType extends Schema {
__type!: TypeOf<this['Item']>[]
abstract Item: SchemaCtor
}参数:
Item- 列表元素的 Schema 类型
返回: 元素类型为 TypeOf<Item>[] 的 List Schema 构造函数
示例:
class Blog extends ObjectType {
tags = List(String) // string[]
scores = List(Number) // number[]
nested = List(List(String)) // string[][]
}Optional - 可选类型
构造函数:
const Optional = <T extends SchemaCtorInput>(Item: T): new () => OptionalType类型:
abstract class OptionalType extends Schema {
__type!: TypeOf<this['Item']> | undefined
abstract Item: SchemaCtor
}参数:
Item- 可选值的 Schema 类型
返回: 类型为 TypeOf<Item> | undefined 的 Optional Schema 构造函数
示例:
class User extends ObjectType {
name = String // 必需
bio = Optional(String) // string | undefined
}
// 生成的 TypeScript 类型
type UserType = {
name: string
bio?: string // 会自动映射为可选属性
}与 Nullable 的区别:
Optional(String)→ 可以省略字段,或提供undefinedNullable(String)→ 必须提供字段,值可以是null
Nullable - 可空类型
构造函数:
const Nullable = <T extends SchemaCtorInput>(Item: T): new () => NullableType类型:
abstract class NullableType extends Schema {
__type!: TypeOf<this['Item']> | null
abstract Item: SchemaCtor
}参数:
Item- 可空值的 Schema 类型
返回: 类型为 TypeOf<Item> | null 的 Nullable Schema 构造函数
示例:
class Article extends ObjectType {
title = String
content = Nullable(String) // string | null
publishedAt = Nullable(Date) // Date | null
}
// 使用
const data1 = { title: "标题" } // ❌ content 字段缺失
const data2 = { title: "标题", content: null } // ✅ 显式提供 nullRecord - 键值对类型
构造函数:
const Record = <T extends SchemaCtorInput>(Item: T): new () => RecordType类型:
abstract class RecordType extends Schema {
__type!: { [key: string]: TypeOf<this['Item']> }
abstract Item: SchemaCtor
}参数:
Item- 值的 Schema 类型
返回: 类型为 { [key: string]: TypeOf<Item> } 的 Record Schema 构造函数
示例:
class Config extends ObjectType {
labels = Record(String) // { [key: string]: string }
counters = Record(Number) // { [key: string]: number }
}Tuple - 元组类型
构造函数:
const Tuple = <T extends SchemaCtorInput[]>(...Items: T): new () => TupleType类型:
abstract class TupleType extends Schema {
__type!: TypeOfTuple<this['Items']>
abstract Items: SchemaCtor[]
}
type TypeOfTuple<T> = T extends []
? []
: T extends [SchemaCtor, ...infer Rest]
? [TypeOf<T[0]>, ...TypeOfTuple<Rest>]
: []参数:
...Items- 元组元素的 Schema 类型数组
返回: 元组 Schema 构造函数
示例:
class Geometry extends ObjectType {
point = Tuple(Number, Number) // [number, number]
rgb = Tuple(Number, Number, Number) // [number, number, number]
mixed = Tuple(String, Number, Boolean) // [string, number, boolean]
}结构化类型 Schema
ObjectType - 对象类型
类型:
abstract class ObjectType extends Schema {
__type!: {
[key in keyof this as SchemaField<this, key>]: TypeOfField<this[key]>
}
}说明:
- 用于定义复杂的嵌套对象结构
- 支持递归引用 - 这是 ObjectType 的核心优势
- 字段可以是 Schema 构造函数、FieldInfo 或嵌套的 FieldDescriptors
- 自动提取所有非
__type的字段
基础用法:
class User extends ObjectType {
id = String
name = String
age = Number
}
type UserType = TypeOf<typeof User>
// { id: string, name: string, age: number }嵌套对象:
class User extends ObjectType {
id = String
name = String
profile = {
bio: String,
avatar: Optional(String),
social: {
twitter: Optional(String),
github: Optional(String)
}
}
}
type UserType = TypeOf<typeof User>
// {
// id: string
// name: string
// profile: {
// bio: string
// avatar?: string
// social: {
// twitter?: string
// github?: string
// }
// }
// }递归引用(自引用):
class Comment extends ObjectType {
id = String
content = String
replies = List(Comment) // ✅ 支持自引用
parent = Optional(Comment) // ✅ 支持可选自引用
}
type CommentType = TypeOf<typeof Comment>
// {
// id: string
// content: string
// replies: CommentType[] // 递归类型
// parent?: CommentType // 递归可选类型
// }相互引用:
class Post extends ObjectType {
title = String
author = User // 引用 User
}
class User extends ObjectType {
name = String
posts = List(Post) // 引用 Post,形成循环引用
}字段元数据:
import { field } from 'farrow-schema'
class User extends ObjectType {
id = ID
name = field({
__type: String,
description: '用户姓名',
deprecated: '请使用 fullName'
})
age = Number
}StructType - 结构体类型
类型:
abstract class StructType extends Schema {
__type!: ShallowPrettier<TypeOfFieldDescriptors<this['descriptors']>>
abstract descriptors: FieldDescriptors
}构造函数:
const Struct = <T extends FieldDescriptors>(descriptors: T): new () => StructType说明:
- 用于快速定义静态结构
- 不支持递归引用 - 这是 Struct 的核心限制
- 适合在联合类型中快速定义变体结构
- 适合运行时动态构建 Schema
基础用法:
const UserStruct = Struct({
id: String,
name: String,
age: Number
})
type UserType = TypeOf<typeof UserStruct>
// { id: string, name: string, age: number }在联合类型中使用(推荐):
const APIResult = Union(
Struct({
success: Literal(true),
data: List(User)
}),
Struct({
success: Literal(false),
error: String
})
)
type APIResultType = TypeOf<typeof APIResult>
// { success: true, data: User[] } | { success: false, error: string }运行时动态构建:
function createValidator(fields: string[]) {
const descriptors: FieldDescriptors = {}
fields.forEach(field => {
descriptors[field] = String
})
return Struct(descriptors)
}
const DynamicSchema = createValidator(['name', 'email', 'phone'])❌ 不支持递归引用:
// ❌ 错误:不支持递归引用
const BadComment = Struct({
id: String,
content: String,
replies: List(BadComment) // ❌ ReferenceError: Cannot access 'BadComment' before initialization
})
// ✅ 正确:使用 ObjectType
class GoodComment extends ObjectType {
id = String
content = String
replies = List(GoodComment) // ✅ 支持递归引用
}ObjectType vs Struct 选择指南:
| 特性 | ObjectType | Struct |
|---|---|---|
| 递归引用 | ✅ 支持 | ❌ 不支持 |
| 定义方式 | class 继承 | 函数调用 |
| 适用场景 | 复杂业务模型、树形结构 | 联合类型变体、动态构建 |
| 性能 | 稍慢(需实例化) | 稍快 |
| 推荐度 | 通用推荐 | 特定场景 |
联合与交集类型
Union - 联合类型
构造函数:
const Union = <T extends SchemaCtorInput[]>(...Items: T): new () => UnionType类型:
abstract class UnionType extends Schema {
__type!: TypeOf<this['Items'][number]>
abstract Items: SchemaCtor[]
}参数:
...Items- 联合类型的成员 Schema 数组
返回: 联合类型 Schema 构造函数
枚举值:
const Status = Union(
Literal('draft'),
Literal('published'),
Literal('archived')
)
type StatusType = TypeOf<typeof Status>
// 'draft' | 'published' | 'archived'判别联合类型(推荐):
const Payment = Union(
Struct({
type: Literal('credit_card'),
cardNumber: String,
cvv: String
}),
Struct({
type: Literal('paypal'),
email: String
})
)
type PaymentType = TypeOf<typeof Payment>
// { type: 'credit_card', cardNumber: string, cvv: string }
// | { type: 'paypal', email: string }
// TypeScript 自动类型窄化
function processPayment(payment: PaymentType) {
switch (payment.type) {
case 'credit_card':
// 这里 payment.cardNumber 和 payment.cvv 可用
console.log(payment.cardNumber)
break
case 'paypal':
// 这里 payment.email 可用
console.log(payment.email)
break
}
}Intersect - 交集类型
构造函数:
const Intersect = <T extends SchemaCtorInput[]>(...Items: T): new () => IntersectType类型:
type UnionToIntersection<T> = (T extends any ? (x: T) => any : never) extends (x: infer R) => any ? R : never
abstract class IntersectType extends Schema {
__type!: UnionToIntersection<TypeOf<this['Items'][number]>>
abstract Items: SchemaCtor[]
}参数:
...Items- 交集类型的成员 Schema 数组
返回: 交集类型 Schema 构造函数
示例:
class BaseUser extends ObjectType {
id = String
name = String
}
class ContactInfo extends ObjectType {
email = String
phone = String
}
// 合并多个类型
const FullUser = Intersect(BaseUser, ContactInfo)
type FullUserType = TypeOf<typeof FullUser>
// { id: string, name: string, email: string, phone: string }Literal - 字面量类型
构造函数:
const Literal = <T extends Literals>(value: T): new () => LiteralType类型:
type Literals = number | string | boolean | null | undefined
abstract class LiteralType extends Schema {
__type!: this['value']
abstract value: Literals
}参数:
value- 字面量值
返回: 字面量类型 Schema 构造函数
示例:
const Environment = Union(
Literal('development'),
Literal('staging'),
Literal('production')
)
const APIResponse = Struct({
status: Literal(200),
message: Literal('OK'),
data: Any
})预定义字面量:
const Null = Literal(null)
const Undefined = Literal(undefined)特殊类型 Schema
Any - 任意类型
class Any extends Schema {
__type!: any
}对应 TypeScript 类型: any验证: 接受任何值
Unknown - 未知类型
class Unknown extends Schema {
__type!: unknown
}对应 TypeScript 类型: unknown验证: 接受任何值,但类型更安全
Never - 永不类型
class Never extends Schema {
__type!: never
}对应 TypeScript 类型: never验证: 不应该验证此类型,会抛出错误
Json - JSON 类型
type JsonType =
| number
| string
| boolean
| null
| undefined
| JsonType[]
| { toJSON(): string }
| { [key: string]: JsonType }
class Json extends Schema {
__type!: JsonType
}对应 TypeScript 类型: JsonType (递归 JSON 类型) 验证: 验证值是否为合法 JSON 值
修饰符类型
Strict - 严格模式
构造函数:
const Strict = <T extends SchemaCtorInput>(Item: T): new () => StrictType类型:
abstract class StrictType extends Schema {
__type!: TypeOf<this['Item']>
abstract Item: SchemaCtor
}说明: 包裹的 Schema 在验证时强制使用严格模式
示例:
class User extends ObjectType {
age = Strict(Number) // 必须是纯数字,不接受 "25" 字符串
}NonStrict - 非严格模式
构造函数:
const NonStrict = <T extends SchemaCtorInput>(Item: T): new () => NonStrictType类型:
abstract class NonStrictType extends Schema {
__type!: TypeOf<this['Item']>
abstract Item: SchemaCtor
}说明: 包裹的 Schema 在验证时强制使用非严格模式
示例:
class User extends ObjectType {
age = NonStrict(Number) // 接受 25 或 "25"
}ReadOnly - 只读类型
构造函数:
const ReadOnly = <T extends SchemaCtorInput>(Item: T): new () => ReadOnlyType类型:
abstract class ReadOnlyType extends Schema {
__type!: Readonly<TypeOf<this['Item']>>
abstract Item: SchemaCtor
}说明: 浅层只读,仅顶层属性只读
ReadOnlyDeep - 深度只读类型
构造函数:
const ReadOnlyDeep = <T extends SchemaCtorInput>(Item: T): new () => ReadOnlyDeepType类型:
abstract class ReadOnlyDeepType extends Schema {
__type!: MarkReadOnlyDeep<TypeOf<this['Item']>>
abstract Item: SchemaCtor
}说明: 深层递归只读,所有嵌套属性都只读
类型工具函数
TypeOf - 提取 TypeScript 类型
类型签名:
type TypeOf<T extends SchemaCtor | Schema> =
T extends DateConstructor ? DateInstanceType
: T extends Primitives ? ReturnType<T>
: T extends new () => { __type: infer U } ? U
: T extends Schema ? T['__type']
: never说明: 从 Schema 构造函数或实例中提取对应的 TypeScript 类型
示例:
class User extends ObjectType {
name = String
age = Number
tags = List(String)
}
type UserType = TypeOf<typeof User>
// { name: string, age: number, tags: string[] }
// 也可以提取嵌套类型
type UserName = UserType['name'] // string
type UserTags = UserType['tags'] // string[]getSchemaCtor - 获取 Schema 构造函数
函数签名:
const getSchemaCtor = <T extends SchemaCtor>(Ctor: T): SchemaTypeOf<T>说明: 将原始类型构造函数(Number、String 等)转换为对应的 Schema 类
示例:
const StringSchema = getSchemaCtor(String) // 返回 farrow-schema 的 String 类
const NumberSchema = getSchemaCtor(Number) // 返回 farrow-schema 的 Number 类
const UserSchema = getSchemaCtor(User) // 返回 User 类本身getInstance - 获取 Schema 实例
函数签名:
const getInstance = <T extends SchemaCtor>(Ctor: T): InstanceTypeOf<T>核心作用: 将 Schema 构造函数转换为运行时实例,提取字段类型信息。采用单例模式(WeakMap 缓存),每个构造函数只实例化一次。
为什么需要:
- Schema 类的字段定义(如
name = String)只存在于实例上 - 构造函数本身无法访问这些字段信息
- 验证器和格式化器需要遍历字段进行处理
示例:
class User extends ObjectType {
name = String
age = Number
tags = List(String)
}
// ❌ 构造函数无法访问字段
console.log(User.name) // undefined
// ✅ 实例可以访问字段定义
const instance = getInstance(User)
console.log(instance.name) // String Schema 构造函数
console.log(instance.age) // Number Schema 构造函数
console.log(instance.tags) // List(String) Schema 构造函数
// 缓存机制:多次调用返回同一实例
const instance2 = getInstance(User)
console.log(instance === instance2) // true内部使用:
// 验证器中
Validator.impl(ObjectType, (schema) => {
// schema = getInstance(User)
// 可以访问 schema.name, schema.age 等字段
return {
validate: (input) => {
for (const key in schema) {
// 遍历字段进行验证
}
}
}
})
// 格式化器中
Formatter.impl(ObjectType, (schema) => {
// schema = getInstance(User)
// 提取字段生成类型元数据
return {
format(ctx) {
for (const [key, field] of Object.entries(schema)) {
// 格式化每个字段
}
}
}
})toSchemaCtor - 转换为 Schema 构造函数
函数签名:
const toSchemaCtor = <T extends SchemaCtorInput>(Item: T): ToSchemaCtor<T>说明:
- 如果输入是 Schema 构造函数,直接返回
- 如果输入是 FieldDescriptors 对象,自动包裹为 Struct
示例:
const StringCtor = toSchemaCtor(String)
// 返回 String Schema
const StructCtor = toSchemaCtor({
name: String,
age: Number
})
// 返回 Struct({ name: String, age: Number })isSchemaCtor - 判断是否是 Schema 构造函数
函数签名:
const isSchemaCtor = (input: any): input is SchemaCtor说明: 类型守卫,判断输入是否为 Schema 构造函数
验证系统 (Validator)
导入路径: farrow-schema/validator
Validator 对象
验证系统的核心对象,提供验证功能。
类型:
const Validator = {
impl<T extends Schema>(
Ctor: abstract new () => T,
impl: ValidatorImpl<T>
): void
get<T extends SchemaCtor>(
Ctor: T
): ValidatorMethods<SchemaTypeOf<T>> | undefined
validate<T extends SchemaCtor>(
Ctor: T,
input: unknown,
options?: ValidatorOptions
): ValidationResult<TypeOf<T>>
}Validator.validate - 验证数据
函数签名:
function validate<T extends SchemaCtor>(
Ctor: T,
input: unknown,
options?: ValidatorOptions
): ValidationResult<TypeOf<T>>参数:
Ctor- Schema 构造函数input- 待验证的数据options- 验证选项strict?: boolean- 是否严格模式(默认false)
返回: Result<T, ValidationError> 类型的验证结果
示例:
import { Validator } from 'farrow-schema/validator'
class User extends ObjectType {
name = String
age = Number
}
// 基础验证
const result = Validator.validate(User, {
name: "张三",
age: 25
})
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('.'))
}
// 严格模式(默认)
const strictResult = Validator.validate(User, {
name: "李四",
age: "30" // ❌ 字符串在严格模式下会失败
})
// 宽松模式
const lenientResult = Validator.validate(User, {
name: "王五",
age: "35" // ✅ 字符串会被转换为数字
},{strict: false})Validator.impl - 实现自定义验证器
函数签名:
function impl<T extends Schema>(
Ctor: abstract new () => T,
impl: ValidatorImpl<T>
): void参数:
Ctor- Schema 构造函数impl- 验证器实现,可以是对象或工厂函数
说明: 为 Schema 类型注册验证器实现(内部使用 WeakMap 存储)
示例:
// 对象形式
Validator.impl(String, {
validate: (input) => {
if (typeof input === 'string') {
return Ok(input)
}
return SchemaErr(`${input} is not a string`)
}
})
// 工厂函数形式(访问 schema 实例)
Validator.impl(LiteralType, (schema) => ({
validate: (input) => {
if (input === schema.value) {
return Ok(input)
}
return SchemaErr(`${input} is not literal ${schema.value}`)
}
}))Validator.get - 获取验证器实现
函数签名:
function get<T extends SchemaCtor>(
Ctor: T
): ValidatorMethods<SchemaTypeOf<T>> | undefined参数:
Ctor- Schema 构造函数
返回: 验证器方法对象或 undefined
说明: 获取 Schema 对应的验证器实现(沿原型链查找)
ValidationResult 类型
类型定义:
type ValidationResult<T = any> = Result<T, ValidationError>
type ValidationError = {
path?: (string | number)[]
message: string
}说明:
path- 错误路径,数组形式,如['user', 'profile', 'age']message- 错误消息
示例:
const result = Validator.validate(User, invalidData)
if (result.kind === 'Err') {
console.log(result.value.message) // "25 is not a string"
console.log(result.value.path) // ["name"]
}ValidatorOptions 类型
类型定义:
type ValidatorOptions = {
strict?: boolean
}说明:
strict: true- 严格模式(默认),不进行类型转换"25"不会转换为25"true"不会转换为true
strict: false- 宽松模式,尝试类型转换"25"→25"true"→true"2024-01-01"→Date
createSchemaValidator - 创建专用验证器
函数签名:
function createSchemaValidator<S extends SchemaCtor>(
SchemaCtor: S,
options?: ValidatorOptions
): (input: unknown) => ValidationResult<TypeOf<S>>参数:
SchemaCtor- Schema 构造函数options- 验证选项
返回: 绑定了 Schema 和选项的验证函数
示例:
// 创建专用验证器
const validateUser = createSchemaValidator(User)
// 在 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 - 自定义验证器基类
类型定义:
abstract class ValidatorType<T = unknown> extends Schema {
__type!: T
abstract validate(input: unknown): ValidationResult<T>
Ok(value: T): ValidationResult<T>
Err(...args: Parameters<typeof SchemaErr>): ValidationResult<T>
}说明: 用于创建自定义验证器的抽象基类
示例:
// 邮箱验证器
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 // 直接使用自定义验证器
}
// 参数化验证器
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)
}RegExp - 正则表达式验证器
函数签名:
const RegExp = (regexp: RegExp): new () => ValidatorType<string>参数:
regexp- 正则表达式
返回: 正则验证器构造函数
示例:
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}$/)
}格式化系统 (Formatter)
导入路径: farrow-schema/formatter
格式化系统用于将 Schema 转换为结构化的类型元数据,常用于:
- 生成 API 文档
- 生成客户端代码
- 类型信息序列化
- GraphQL Schema 生成
Formatter 对象
格式化系统的核心对象。
类型:
const Formatter = {
impl<T extends Schema>(
Ctor: abstract new () => T,
impl: FormatterImpl<T>
): void
get<T extends SchemaCtor>(
Ctor: T
): FormatterMethods | undefined
formatSchema<T extends SchemaCtor>(
Ctor: T,
ctx: FormatContext
): number
format<T extends SchemaCtor>(
Ctor: T,
context?: FormatContext
): { typeId: number, types: FormatTypes }
}Formatter.format - 格式化 Schema
函数签名:
function format<T extends SchemaCtor>(
Ctor: T,
context?: FormatContext
): {
typeId: number
types: FormatTypes
}
type FormatTypes = {
[key: string]: FormatType
}参数:
Ctor- Schema 构造函数context- 格式化上下文(可选)
返回:
typeId- 根类型的 IDtypes- 类型字典,键为类型 ID(字符串形式),值为格式化类型
示例:
import { Formatter } from 'farrow-schema/formatter'
class User extends ObjectType {
id = ID
name = String
age = Number
tags = List(String)
}
const { typeId, types } = Formatter.format(User)
console.log(typeId) // 0
console.log(types)
/*
{
"0": {
type: "Object",
name: "User",
fields: {
id: { typeId: 1, $ref: "#/types/1", description: undefined },
name: { typeId: 2, $ref: "#/types/2", description: undefined },
age: { typeId: 3, $ref: "#/types/3", description: undefined },
tags: { typeId: 4, $ref: "#/types/4", description: undefined }
}
},
"1": { type: "Scalar", valueType: "string", valueName: "ID" },
"2": { type: "Scalar", valueType: "string", valueName: "String" },
"3": { type: "Scalar", valueType: "number", valueName: "Number" },
"4": { type: "List", itemTypeId: 2, $ref: "#/types/2" }
}
*/复杂示例:
class Article extends ObjectType {
title = String
author = User // 嵌套 ObjectType
tags = List(String)
status = Union(
Literal('draft'),
Literal('published')
)
}
const { typeId, types } = Formatter.format(Article)
/*
types 包含:
- Article ObjectType (typeId: 0)
- String Scalar (typeId: 1)
- User ObjectType (typeId: 2)
- ID Scalar (typeId: 3)
- Number Scalar (typeId: 4)
- List<String> (typeId: 5)
- Union (typeId: 6)
- Literal('draft') (typeId: 7)
- Literal('published') (typeId: 8)
*/formatSchema - format 的别名
函数签名:
const formatSchema = Formatter.format说明: Formatter.format 的导出别名
示例:
import { formatSchema } from 'farrow-schema/formatter'
const result = formatSchema(User)Formatter.formatSchema - 格式化 Schema(上下文版本)
函数签名:
function formatSchema<T extends SchemaCtor>(
Ctor: T,
ctx: FormatContext
): number参数:
Ctor- Schema 构造函数ctx- 格式化上下文(必需)
返回: 类型 ID
说明:
- 在给定上下文中格式化 Schema
- 使用上下文的
addType和formatCache - 用于在自定义格式化器中格式化嵌套类型
Formatter.impl - 实现自定义格式化器
函数签名:
function impl<T extends Schema>(
Ctor: abstract new () => T,
impl: FormatterImpl<T>
): void
type FormatterImpl<T extends Schema = Schema> =
| FormatterMethods
| ((schema: T) => FormatterMethods)
type FormatterMethods = {
format(context: FormatContext): number
}参数:
Ctor- Schema 构造函数impl- 格式化器实现
说明: 为 Schema 类型注册格式化器实现
示例:
// 对象形式
Formatter.impl(String, {
format(ctx) {
return ctx.addType({
type: 'Scalar',
valueType: 'string',
valueName: 'String',
})
}
})
// 工厂函数形式(访问 schema 实例)
Formatter.impl(LiteralType, (schema) => ({
format(ctx) {
return ctx.addType({
type: 'Literal',
value: schema.value,
})
}
}))
// 嵌套类型格式化
Formatter.impl(ListType, (schema) => ({
format(ctx) {
// 先格式化元素类型
const itemTypeId = Formatter.formatSchema(schema.Item, ctx)
// 再添加 List 类型
return ctx.addType({
type: 'List',
itemTypeId: itemTypeId,
$ref: `#/types/${itemTypeId}`,
})
}
}))Formatter.get - 获取格式化器实现
函数签名:
function get<T extends SchemaCtor>(
Ctor: T
): FormatterMethods | undefined参数:
Ctor- Schema 构造函数
返回: 格式化器方法对象或 undefined
说明: 获取 Schema 对应的格式化器实现(沿原型链查找)
FormatContext - 格式化上下文
类型定义:
type FormatContext = {
addType: (type: FormatType) => number
formatCache: WeakMap<Function, number>
}说明:
addType- 添加类型到类型字典,返回分配的类型 IDformatCache- 类型缓存,避免重复格式化同一类型
使用场景: 在自定义格式化器中使用
FormatType - 格式化类型(联合类型)
类型定义:
type FormatType =
| FormatScalarType
| FormatObjectType
| FormatUnionType
| FormatStructType
| FormatRecordType
| FormatListType
| FormatLiteralType
| FormatNullableType
| FormatOptionalType
| FormatIntersectType
| FormatTupleType
| FormatStrictType
| FormatNonStrictType
| FormatReadOnlyType
| FormatReadonlyDeepType说明: 所有格式化类型的联合,每种类型都有 type 判别字段
格式化类型详解
FormatScalarType - 标量类型格式
类型定义:
type FormatScalarType = {
type: 'Scalar'
valueType: string // TypeScript 类型字符串
valueName: string // Schema 名称
}对应 Schema: String, Number, Int, Float, Boolean, ID, Date, Any, Unknown, Never, Json
示例:
// String Schema
{ type: 'Scalar', valueType: 'string', valueName: 'String' }
// Number Schema
{ type: 'Scalar', valueType: 'number', valueName: 'Number' }
// ID Schema
{ type: 'Scalar', valueType: 'string', valueName: 'ID' }FormatObjectType - 对象类型格式
类型定义:
type FormatObjectType = {
type: 'Object'
name: string
fields: FormatFields
namespace?: string
}
type FormatFields = {
[key: string]: FormatField
}
type FormatField = {
typeId: number
$ref: string
description?: string
deprecated?: string
}对应 Schema: ObjectType
字段说明:
name- ObjectType 的类名或 displayNamefields- 对象字段字典- 键为字段名
- 值为字段格式信息
namespace- 命名空间(如果设置)
示例:
class User extends ObjectType {
id = ID
name = field({
__type: String,
description: '用户名称'
})
}
User.displayName = 'UserModel'
User.namespace = 'com.example'
const { types } = Formatter.format(User)
/*
types["0"] = {
type: "Object",
name: "UserModel",
namespace: "com.example",
fields: {
id: {
typeId: 1,
$ref: "#/types/1",
description: undefined,
deprecated: undefined
},
name: {
typeId: 2,
$ref: "#/types/2",
description: "用户名称",
deprecated: undefined
}
}
}
*/FormatStructType - 结构体类型格式
类型定义:
type FormatStructType = {
type: 'Struct'
name?: string
fields: FormatFields
namespace?: string
}对应 Schema: StructType
说明: 与 FormatObjectType 类似,但 name 是可选的
示例:
const UserStruct = Struct({
id: ID,
name: String
})
const { types } = Formatter.format(UserStruct)
/*
types["0"] = {
type: "Struct",
name: undefined, // Struct 默认没有名称
fields: { ... }
}
*/FormatListType - 列表类型格式
类型定义:
type FormatListType = {
type: 'List'
itemTypeId: number
$ref: string
}对应 Schema: List
字段说明:
itemTypeId- 列表元素类型的 ID$ref- 元素类型的引用路径
示例:
class Blog extends ObjectType {
tags = List(String)
}
const { types } = Formatter.format(Blog)
/*
types["2"] = {
type: "List",
itemTypeId: 1, // String 的 typeId
$ref: "#/types/1"
}
*/FormatNullableType - 可空类型格式
类型定义:
type FormatNullableType = {
type: 'Nullable'
itemTypeId: number
$ref: string
}对应 Schema: Nullable
FormatOptionalType - 可选类型格式
类型定义:
type FormatOptionalType = {
type: 'Optional'
itemTypeId: number
$ref: string
}对应 Schema: Optional
FormatLiteralType - 字面量类型格式
类型定义:
type FormatLiteralType = {
type: 'Literal'
value: Literals
}
type Literals = number | string | boolean | null | undefined对应 Schema: Literal
示例:
const Status = Literal('active')
const { types } = Formatter.format(Status)
/*
types["0"] = {
type: "Literal",
value: "active"
}
*/FormatUnionType - 联合类型格式
类型定义:
type FormatUnionType = {
type: 'Union'
name?: string
itemTypes: { typeId: number; $ref: string }[]
namespace?: string
}对应 Schema: Union
字段说明:
itemTypes- 联合类型成员数组- 每个成员包含
typeId和$ref
- 每个成员包含
示例:
const Status = Union(
Literal('draft'),
Literal('published')
)
const { types } = Formatter.format(Status)
/*
types["0"] = {
type: "Union",
name: undefined,
itemTypes: [
{ typeId: 1, $ref: "#/types/1" }, // Literal('draft')
{ typeId: 2, $ref: "#/types/2" } // Literal('published')
]
}
*/FormatIntersectType - 交集类型格式
类型定义:
type FormatIntersectType = {
type: 'Intersect'
name?: string
itemTypes: { typeId: number; $ref: string }[]
namespace?: string
}对应 Schema: Intersect
FormatTupleType - 元组类型格式
类型定义:
type FormatTupleType = {
type: 'Tuple'
name?: string
itemTypes: { typeId: number; $ref: string }[]
namespace?: string
}对应 Schema: Tuple
示例:
const Point = Tuple(Number, Number)
const { types } = Formatter.format(Point)
/*
types["0"] = {
type: "Tuple",
name: undefined,
itemTypes: [
{ typeId: 1, $ref: "#/types/1" }, // Number
{ typeId: 1, $ref: "#/types/1" } // Number (same typeId, cached)
]
}
*/FormatRecordType - Record 类型格式
类型定义:
type FormatRecordType = {
type: 'Record'
valueTypeId: number
$ref: string
}对应 Schema: Record
FormatStrictType - 严格类型格式
类型定义:
type FormatStrictType = {
type: 'Strict'
itemTypeId: number
$ref: string
}对应 Schema: Strict
FormatNonStrictType - 非严格类型格式
类型定义:
type FormatNonStrictType = {
type: 'NonStrict'
itemTypeId: number
$ref: string
}对应 Schema: NonStrict
FormatReadOnlyType - 只读类型格式
类型定义:
type FormatReadOnlyType = {
type: 'ReadOnly'
itemTypeId: number
$ref: string
}对应 Schema: ReadOnly
FormatReadonlyDeepType - 深度只读类型格式
类型定义:
type FormatReadonlyDeepType = {
type: 'ReadOnlyDeep'
itemTypeId: number
$ref: string
}对应 Schema: ReadOnlyDeep
isNamedFormatType - 判断是否是命名格式类型
函数签名:
const isNamedFormatType = (input: FormatType): input is NamedFormatType
type NamedFormatType =
| FormatTupleType
| FormatStructType
| FormatUnionType
| FormatIntersectType
| FormatObjectType说明: 类型守卫,判断格式类型是否可以有名称
示例:
import { isNamedFormatType } from 'farrow-schema/formatter'
const { types } = Formatter.format(User)
for (const [id, formatType] of Object.entries(types)) {
if (isNamedFormatType(formatType)) {
console.log(`Type ${id} name: ${formatType.name}`)
}
}Formatter 实战示例
生成 API 文档
import { Formatter } from 'farrow-schema/formatter'
class User extends ObjectType {
id = ID
name = String
email = String
}
class CreateUserInput extends ObjectType {
name = String
email = String
}
const APIEndpoint = Struct({
path: Literal('/api/users'),
method: Literal('POST'),
input: CreateUserInput,
output: User
})
const { typeId, types } = Formatter.format(APIEndpoint)
// 遍历类型生成文档
function generateDocs(types: FormatTypes) {
for (const [id, formatType] of Object.entries(types)) {
switch (formatType.type) {
case 'Object':
console.log(`Object: ${formatType.name}`)
for (const [fieldName, field] of Object.entries(formatType.fields)) {
const fieldType = types[field.typeId]
console.log(` ${fieldName}: ${getTypeName(fieldType)}`)
if (field.description) {
console.log(` Description: ${field.description}`)
}
}
break
case 'Literal':
console.log(`Literal: ${formatType.value}`)
break
// ... 其他类型
}
}
}
function getTypeName(formatType: FormatType): string {
switch (formatType.type) {
case 'Scalar':
return formatType.valueName
case 'Object':
return formatType.name
case 'List':
return `List<${formatType.itemTypeId}>`
// ... 其他类型
default:
return 'Unknown'
}
}辅助工具 (Helper)
导入路径: farrow-schema(主入口自动导出)
Helper 模块提供了一系列操作 Schema 的工具函数。
field - 字段定义辅助函数
函数签名:
const field = <T extends FieldInfo>(fieldInfo: T): T
type FieldInfo = {
__type: SchemaCtor
description?: string
deprecated?: string
}参数:
fieldInfo- 字段信息对象__type- 字段的 Schema 类型description- 字段描述(可选)deprecated- 弃用说明(可选)
返回: 原样返回 fieldInfo(用于类型推导)
示例:
import { field } from 'farrow-schema'
class User extends ObjectType {
id = ID
name = field({
__type: String,
description: '用户姓名'
})
email = field({
__type: String,
description: '用户邮箱',
deprecated: '请使用 contactEmail'
})
}
// 格式化时会包含 description 和 deprecated 信息
const { types } = Formatter.format(User)pick 系列 - 选择字段
pickObject - 选择 ObjectType 字段
函数签名:
const pickObject = <T extends ObjectType, Keys extends SchemaField<T, keyof T>[]>(
Ctor: new () => T,
keys: Keys
): PickObject<T, Keys>参数:
Ctor- ObjectType 构造函数keys- 要选择的字段名数组
返回: 新的 ObjectType 构造函数,仅包含选择的字段
示例:
import { pickObject } from 'farrow-schema'
class FullUser extends ObjectType {
id = String
name = String
email = String
password = String
createdAt = Date
}
// 选择公开字段
const PublicUser = pickObject(FullUser, ['id', 'name'])
type PublicUserType = TypeOf<typeof PublicUser>
// { id: string, name: string }pickStruct - 选择 Struct 字段
函数签名:
const pickStruct = <T extends StructType, Keys extends (keyof T['descriptors'])[]>(
Ctor: new () => T,
keys: Keys
): new () => StructType参数:
Ctor- StructType 构造函数keys- 要选择的字段名数组
返回: 新的 StructType 构造函数,仅包含选择的字段
pick - 通用选择函数
函数签名:
const pick: typeof pickObject & typeof pickStruct说明: 自动判断是 ObjectType 还是 StructType,调用对应函数
⚠️ 类型推导限制: 由于 TypeScript 的限制,pick 通用函数在输入第二个参数(字段名数组)时无法提供准确的类型提示和自动补全。
推荐做法:
- 对于 ObjectType,使用
pickObject - 对于 StructType,使用
pickStruct
这样可以获得完整的类型推导和 IDE 智能提示。
示例:
import { pick, pickObject, pickStruct } from 'farrow-schema'
// ❌ 不推荐:第二个参数缺少类型提示
const PublicUser = pick(FullUser, ['id', 'name'])
// ✅ 推荐:使用专用函数,获得字段名自动补全
const PublicUser = pickObject(FullUser, ['id', 'name']) // IDE 会提示可用字段
const PublicUserStruct = pickStruct(FullUserStruct, ['id', 'name'])omit 系列 - 排除字段
omitObject - 排除 ObjectType 字段
函数签名:
const omitObject = <T extends ObjectType, Keys extends SchemaField<T, keyof T>[]>(
Ctor: new () => T,
keys: Keys
): OmitObject<T, Keys>参数:
Ctor- ObjectType 构造函数keys- 要排除的字段名数组
返回: 新的 ObjectType 构造函数,不包含排除的字段
示例:
import { omitObject } from 'farrow-schema'
class FullUser extends ObjectType {
id = String
name = String
email = String
password = String
createdAt = Date
}
// 排除敏感字段
const SafeUser = omitObject(FullUser, ['password'])
type SafeUserType = TypeOf<typeof SafeUser>
// { id: string, name: string, email: string, createdAt: Date }
// 排除系统字段
const UserInput = omitObject(FullUser, ['id', 'createdAt'])
type UserInputType = TypeOf<typeof UserInput>
// { name: string, email: string, password: string }omitStruct - 排除 Struct 字段
函数签名:
const omitStruct = <T extends StructType, Keys extends (keyof T['descriptors'])[]>(
Ctor: new () => T,
keys: Keys
): new () => StructTypeomit - 通用排除函数
函数签名:
const omit: typeof omitObject & typeof omitStruct说明: 自动判断是 ObjectType 还是 StructType,调用对应函数
⚠️ 类型推导限制: 由于 TypeScript 的限制,omit 通用函数在输入第二个参数(字段名数组)时无法提供准确的类型提示和自动补全。
推荐做法:
- 对于 ObjectType,使用
omitObject - 对于 StructType,使用
omitStruct
这样可以获得完整的类型推导和 IDE 智能提示。
示例:
import { omit, omitObject, omitStruct } from 'farrow-schema'
// ❌ 不推荐:第二个参数缺少类型提示
const SafeUser = omit(FullUser, ['password'])
// ✅ 推荐:使用专用函数,获得字段名自动补全
const SafeUser = omitObject(FullUser, ['password']) // IDE 会提示可用字段
const SafeUserStruct = omitStruct(FullUserStruct, ['password'])partial 系列 - 转为可选字段
partialObject - ObjectType 可选化
函数签名:
const partialObject = <T extends ObjectType>(
Ctor: new () => T
): PartialObjectType<T>参数:
Ctor- ObjectType 构造函数
返回: 新的 ObjectType 构造函数,所有字段变为可选
示例:
import { partialObject } from 'farrow-schema'
class User extends ObjectType {
id = String
name = String
email = String
}
const PartialUser = partialObject(User)
type PartialUserType = TypeOf<typeof PartialUser>
// { id?: string, name?: string, email?: string }partialStruct - Struct 可选化
函数签名:
const partialStruct = <T extends StructType>(
Ctor: new () => T
): new () => PartialTypepartial - 通用可选化函数
函数签名:
const partial: typeof partialObject & typeof partialStruct示例:
import { partial } from 'farrow-schema'
const PartialUser = partial(User)
const PartialUserStruct = partial(UserStruct)required 系列 - 转为必填字段
requiredObject - ObjectType 必填化
函数签名:
const requiredObject = <T extends ObjectType>(
Ctor: new () => T
): RequiredObjectType<T>参数:
Ctor- ObjectType 构造函数
返回: 新的 ObjectType 构造函数,所有可选字段变为必填
示例:
import { requiredObject } from 'farrow-schema'
class OptionalUser extends ObjectType {
id = Optional(String)
name = Optional(String)
email = Optional(String)
}
const RequiredUser = requiredObject(OptionalUser)
type RequiredUserType = TypeOf<typeof RequiredUser>
// { id: string, name: string, email: string }requiredStruct - Struct 必填化
函数签名:
const requiredStruct = <T extends StructType>(
Ctor: new () => T
): new () => StructTyperequired - 通用必填化函数
函数签名:
const required: typeof requiredObject & typeof requiredStruct示例:
import { required } from 'farrow-schema'
const RequiredUser = required(OptionalUser)keyof 系列 - 获取字段键
keyofObject - 获取 ObjectType 字段键
函数签名:
const keyofObject = <T extends ObjectType>(
Ctor: new () => T
): (keyof TypeOf<T>)[]参数:
Ctor- ObjectType 构造函数
返回: 字段名数组
示例:
import { keyofObject } from 'farrow-schema'
class User extends ObjectType {
id = String
name = String
email = String
}
const keys = keyofObject(User)
// ['id', 'name', 'email']keyofStruct - 获取 Struct 字段键
函数签名:
const keyofStruct = <T extends StructType>(
Ctor: new () => T
): (keyof T['descriptors'])[]keyof - 通用获取键函数
函数签名:
const keyof: typeof keyofObject & typeof keyofStruct示例:
import { keyof } from 'farrow-schema'
const userKeys = keyof(User)
const structKeys = keyof(UserStruct)Result 类型
导入路径: farrow-schema(主入口自动导出)
Result 类型提供函数式错误处理模式。
Result 类型定义
类型:
type Result<T = any, E = string> = Ok<T> | Err<E>
type Ok<T = any> = {
kind: 'Ok'
value: T
isOk: true
isErr: false
}
type Err<E = any> = {
kind: 'Err'
value: E
isOk: false
isErr: true
}说明:
Ok- 成功结果,包含成功值Err- 错误结果,包含错误值kind- 判别字段,用于类型收窄(推荐)isOk/isErr- 布尔类型守卫属性(向后兼容)
Ok - 创建成功结果
函数签名:
const Ok = <T, E = string>(value: T): Result<T, E>参数:
value- 成功值
返回: Ok 结果
示例:
import { Ok } from 'farrow-schema'
const result = Ok(42)
// { kind: 'Ok', value: 42, isOk: true, isErr: false }Err - 创建错误结果
函数签名:
const Err = <E = string>(value: E): Err<E>参数:
value- 错误值
返回: Err 结果
示例:
import { Err } from 'farrow-schema'
const result = Err('Something went wrong')
// { kind: 'Err', value: 'Something went wrong', isOk: false, isErr: true }Result 使用模式
类型收窄(推荐):
import { Result, Ok, Err } from 'farrow-schema'
function divide(a: number, b: number): Result<number, string> {
if (b === 0) {
return Err('Division by zero')
}
return Ok(a / b)
}
const result = divide(10, 2)
// 推荐:使用 kind 字段进行类型收窄
if (result.kind === 'Ok') {
console.log(result.value) // 类型为 number
} else {
console.log(result.value) // 类型为 string
}
// 也可以使用 isOk/isErr(向后兼容)
if (result.isOk) {
console.log(result.value) // 类型为 number
}业务逻辑中使用:
import { Validator } from 'farrow-schema/validator'
import { Result, Ok, Err } from 'farrow-schema'
function processUser(data: unknown): Result<UserType, string> {
const validationResult = Validator.validate(User, data)
if (validationResult.kind === 'Err') {
return Err(`数据验证失败: ${validationResult.value.message}`)
}
const user = validationResult.value
if (user.age < 18) {
return Err('用户年龄必须大于18岁')
}
return Ok(user)
}
const result = processUser(rawData)
if (result.kind === 'Ok') {
// 使用 user
console.log(result.value)
} else {
// 处理错误
console.log(result.value)
}类型工具
导入路径: farrow-schema(主入口自动导出)
DateInstanceType
类型定义:
type DateInstanceType = InstanceType<typeof Date>说明: Date 实例类型的别名,等同于 Date
MarkReadOnlyDeep
类型定义:
type MarkReadOnlyDeep<T> = T extends Basic | ((...args: any[]) => unknown)
? T
: T extends ReadonlyMap<infer KeyType, infer ValueType>
? ReadOnlyMapDeep<KeyType, ValueType>
: T extends ReadonlySet<infer ItemType>
? ReadOnlySetDeep<ItemType>
: T extends {}
? ReadOnlyObjectDeep<T>
: unknown
type Basic = null | undefined | string | number | boolean | symbol | bigint说明: 深度递归只读类型工具
示例:
type User = {
name: string
profile: {
bio: string
tags: string[]
}
}
type ReadOnlyUser = MarkReadOnlyDeep<User>
/*
{
readonly name: string
readonly profile: {
readonly bio: string
readonly tags: readonly string[]
}
}
*/使用模式与最佳实践
1. Schema 定义模式
✅ 推荐:使用 ObjectType 定义业务模型
// 推荐:清晰的业务模型
class User extends ObjectType {
id = ID
name = String
email = String
profile = {
bio: String,
avatar: Optional(String)
}
}
// 不推荐:使用 Struct(不支持递归)
const User = Struct({
id: ID,
name: String
})✅ 推荐:使用字段元数据
class User extends ObjectType {
id = ID
email = field({
__type: String,
description: '用户邮箱地址',
deprecated: '请使用 contactEmail'
})
}2. 验证模式
✅ 推荐:创建专用验证器
// 推荐:提前创建验证器,复用
const validateUser = createSchemaValidator(User)
app.post('/users', (req, res) => {
const result = validateUser(req.body)
if (result.kind === 'Err') {
return res.status(400).json(result.value)
}
// ...
})
// 不推荐:每次都调用 Validator.validate
app.post('/users', (req, res) => {
const result = Validator.validate(User, req.body)
// ...
})✅ 推荐:使用 Result 类型进行错误处理
// 推荐:使用 kind 字段收窄
if (result.kind === 'Ok') {
console.log(result.value)
} else {
console.log(result.value.message)
}3. 自定义验证器模式
✅ 推荐:继承 ValidatorType
class EmailType extends ValidatorType<string> {
validate(input: unknown) {
const result = Validator.validate(String, input)
if (result.kind === 'Err') return result
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(result.value)) {
return this.Err('Invalid email format')
}
return this.Ok(result.value)
}
}
// 使用
class User extends ObjectType {
email = EmailType
}4. 类型转换模式
✅ 推荐:使用辅助函数进行类型派生
class User extends ObjectType {
id = ID
name = String
email = String
password = String
createdAt = Date
}
// 派生公开类型
const PublicUser = omitObject(User, ['password'])
// 派生创建输入类型
const CreateUserInput = omitObject(User, ['id', 'createdAt'])
// 派生更新输入类型
const UpdateUserInput = partialObject(
omitObject(User, ['id', 'createdAt'])
)5. 递归类型模式
✅ 推荐:使用 ObjectType(支持递归)
// 树形结构
class TreeNode extends ObjectType {
value = String
children = List(TreeNode) // ✅ 递归引用
}
// 评论系统
class Comment extends ObjectType {
id = ID
content = String
author = User
replies = List(Comment) // ✅ 递归引用
parent = Optional(Comment) // ✅ 可选递归引用
}6. 联合类型模式
✅ 推荐:使用判别联合类型
// 推荐:使用 kind 字段作为判别器
const APIResponse = Union(
Struct({
type: Literal('success'),
data: User
}),
Struct({
type: Literal('error'),
message: String,
code: Number
})
)
// TypeScript 自动类型窄化
function handle(response: TypeOf<typeof APIResponse>) {
switch (response.type) {
case 'success':
console.log(response.data) // ✅ 类型正确
break
case 'error':
console.log(response.message) // ✅ 类型正确
break
}
}7. 格式化模式
✅ 推荐:使用格式化器生成 API 文档
import { Formatter, isNamedFormatType } from 'farrow-schema/formatter'
const { typeId, types } = Formatter.format(User)
// 生成 OpenAPI Schema
function toOpenAPISchema(types: FormatTypes, rootId: number) {
const rootType = types[rootId]
if (rootType.type === 'Object') {
const properties: any = {}
for (const [name, field] of Object.entries(rootType.fields)) {
properties[name] = convertField(types[field.typeId])
}
return { type: 'object', properties }
}
// ... 其他类型转换
}常见问题
Q: Optional 和 Nullable 有什么区别?
A:
Optional(String)→ 字段可以省略或为undefinedNullable(String)→ 字段必须提供,值可以是null
class User extends ObjectType {
bio = Optional(String) // 可以省略
deletedAt = Nullable(Date) // 必须提供,可以为 null
}
// ✅ 合法
{ bio: undefined }
{ deletedAt: null }
// ❌ 非法
{ bio: null } // bio 不接受 null
{ } // deletedAt 字段缺失Q: 如何提取嵌套类型?
A: 使用 TypeOf 结合 TypeScript 的索引访问:
class User extends ObjectType {
profile = {
bio: String,
social: {
twitter: Optional(String),
github: Optional(String)
}
}
}
type UserType = TypeOf<typeof User>
type ProfileType = UserType['profile']
type SocialType = UserType['profile']['social']
type TwitterType = UserType['profile']['social']['twitter'] // string | undefinedQ: 如何派生多个变体类型?
A: 使用辅助函数链式组合:
class FullUser extends ObjectType {
id = ID
name = String
email = String
password = String
createdAt = Date
updatedAt = Date
}
// 公开用户信息
const PublicUser = omitObject(FullUser, ['password'])
// 创建输入(无 ID 和时间戳)
const CreateUserInput = omitObject(FullUser, ['id', 'createdAt', 'updatedAt', 'password'])
// 更新输入(部分字段,无 ID 和时间戳)
const UpdateUserInput = partialObject(
omitObject(FullUser, ['id', 'createdAt', 'updatedAt', 'password'])
)
// 登录输入(仅邮箱和密码)
const LoginInput = pickObject(FullUser, ['email', 'password'])Q: 格式化器的输出如何使用?
A: 格式化器输出可用于多种场景:
const { typeId, types } = Formatter.format(User)
// 1. 生成 API 文档
function generateDocs(types: FormatTypes) {
for (const [id, formatType] of Object.entries(types)) {
if (formatType.type === 'Object') {
console.log(`## ${formatType.name}`)
for (const [name, field] of Object.entries(formatType.fields)) {
console.log(`- ${name}: ${field.description || ''}`)
}
}
}
}
// 2. 生成客户端 SDK
function generateClient(types: FormatTypes, rootId: number) {
// 根据 types 生成 TypeScript 接口、API 调用函数等
}
// 3. 转换为其他格式(JSON Schema、GraphQL Schema 等)
function toJSONSchema(types: FormatTypes, rootId: number) {
// ...
}总结
farrow-schema 提供了强大而灵活的类型系统,让你能够:
- 类型即文档 - Schema 定义同时作为 TypeScript 类型和运行时验证规则
- 声明式建模 - 使用声明式语法定义复杂数据结构
- 三位一体 - 类型定义、运行时验证、元数据提取统一在一个系统中
- 递归友好 - 天然支持递归和相互引用的复杂类型
- 可组合性 - Schema 可以自由组合、转换、派生
- 错误处理 - 函数式 Result 类型,优雅处理异常
通过这些特性,farrow-schema 能够帮你构建类型安全、可维护的应用程序。
