Skip to content

farrow-schema 完整 API 参考 ​

目录 ​

  1. 核心概念
  2. 完整导出清单
  3. Schema 类型系统
  4. 验证系统 (Validator)
  5. 格式化系统 (Formatter)
  6. 辅助工具 (Helper)
  7. Result 类型
  8. 使用模式与最佳实践
  9. 常见问题

核心概念 ​

设计哲学 ​

farrow-schema 是一个类型安全的运行时验证与序列化系统,核心设计理念:

  1. 类型即文档 - Schema 定义同时作为 TypeScript 类型和运行时验证规则
  2. 声明式建模 - 使用声明式语法定义复杂数据结构
  3. 三位一体 - 类型定义、运行时验证、元数据提取统一在一个系统中
  4. 递归友好 - 天然支持递归和相互引用的复杂类型
  5. 可组合性 - Schema 可以自由组合、转换、派生

三大核心系统 ​

Schema (类型定义)  →  TypeScript 类型
Validator (验证)   →  运行时数据校验
Formatter (格式化) →  API 文档生成

模块架构 ​

farrow-schema/
├── index.ts              # 主入口:类型定义系统
├── validator.ts          # 验证器入口:运行时验证
└── formatter.ts          # 格式化器入口:类型元数据提取

完整导出清单 ​

主入口 (farrow-schema) ​

导入路径: farrow-schema

核心职责: 提供类型定义系统、类型工具、辅助函数和 Result 类型

典型使用:

typescript
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'

核心导出分类:

  1. Schema 类 - 类型定义

    • 基础类型: String, Number, Boolean, Date, ID, Int, Float
    • 特殊类型: Any, Unknown, Never, Json
    • 抽象类: Schema, ObjectType 等
  2. 构造函数 - 类型组合

    • List(Item) - 列表类型
    • Optional(Item) - 可选类型
    • Nullable(Item) - 可空类型
    • Record(Item) - 键值对类型
    • Tuple(...Items) - 元组类型
    • Union(...Items) - 联合类型
    • Intersect(...Items) - 交集类型
    • Literal(value) - 字面量类型
    • Struct(descriptors) - 结构体类型
  3. 类型工具 - 类型提取与判断

    • TypeOf<T> - 提取 TypeScript 类型
    • getInstance(Ctor) - 获取 Schema 实例
    • getSchemaCtor(Ctor) - 获取 Schema 构造函数
    • isSchemaCtor(input) - 判断是否为 Schema 构造函数
  4. 辅助函数 - 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)取字段键
  5. Result 类型 - 错误处理

    • Result<T, E> - 结果类型
    • Ok(value) - 成功结果
    • Err(value) - 错误结果

验证器入口 (farrow-schema/validator) ​

导入路径: farrow-schema/validator

核心职责: 提供运行时数据验证功能

典型使用:

typescript
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 元数据提取与序列化功能,用于文档生成、代码生成、跨语言类型共享

典型使用:

typescript
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?) - 格式化 Schema
    • Formatter.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 - 字段格式信息

应用场景:

  1. API 文档生成 - 自动生成 OpenAPI/Swagger 文档
  2. 客户端代码生成 - 生成 TypeScript/JavaScript SDK
  3. GraphQL Schema - 转换为 GraphQL 类型定义
  4. 跨语言类型共享 - 导出为 JSON Schema、Protocol Buffers 等
  5. 类型可视化 - 生成类型关系图

模块选择指南 ​

需求使用模块典型场景
定义数据结构farrow-schema定义 API 输入输出类型、业务模型
验证运行时数据farrow-schema/validatorAPI 参数验证、表单验证
提取类型元数据farrow-schema/formatter生成文档、代码生成、跨语言类型共享
完整功能全部导入完整的类型安全系统

完整使用示例:

typescript
// 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 的基类。

类型签名:

typescript
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 的值

使用示例:

typescript
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 - 数值类型 ​

typescript
class Number extends Schema {
  __type!: number
}

对应 TypeScript 类型: number

示例:

typescript
class Product extends ObjectType {
  price = Number
}

Int - 整数类型 ​

typescript
class Int extends Schema {
  __type!: number
}

对应 TypeScript 类型: number验证: 验证时会检查是否为整数,非严格模式会向下取整

示例:

typescript
class Pagination extends ObjectType {
  page = Int
  limit = Int
}

Float - 浮点数类型 ​

typescript
class Float extends Schema {
  __type!: number
}

对应 TypeScript 类型: number验证: 与 Number 相同


String - 字符串类型 ​

typescript
class String extends Schema {
  __type!: string
}

对应 TypeScript 类型: string


Boolean - 布尔类型 ​

typescript
class Boolean extends Schema {
  __type!: boolean
}

对应 TypeScript 类型: boolean


ID - 标识符类型 ​

typescript
class ID extends Schema {
  __type!: string
}

对应 TypeScript 类型: string验证: 不能为空字符串

示例:

typescript
class User extends ObjectType {
  id = ID
  name = String
}

Date - 日期类型 ​

typescript
class Date extends Schema {
  __type!: DateInstanceType
}

对应 TypeScript 类型: Date验证: 接受 Date 实例、时间戳数字、ISO 日期字符串

示例:

typescript
class Event extends ObjectType {
  title = String
  startDate = Date
}

复合类型 Schema ​

List - 列表类型 ​

构造函数:

typescript
const List = <T extends SchemaCtorInput>(Item: T): new () => ListType

类型:

typescript
abstract class ListType extends Schema {
  __type!: TypeOf<this['Item']>[]
  abstract Item: SchemaCtor
}

参数:

  • Item - 列表元素的 Schema 类型

返回: 元素类型为 TypeOf<Item>[] 的 List Schema 构造函数

示例:

typescript
class Blog extends ObjectType {
  tags = List(String)              // string[]
  scores = List(Number)            // number[]
  nested = List(List(String))      // string[][]
}

Optional - 可选类型 ​

构造函数:

typescript
const Optional = <T extends SchemaCtorInput>(Item: T): new () => OptionalType

类型:

typescript
abstract class OptionalType extends Schema {
  __type!: TypeOf<this['Item']> | undefined
  abstract Item: SchemaCtor
}

参数:

  • Item - 可选值的 Schema 类型

返回: 类型为 TypeOf<Item> | undefined 的 Optional Schema 构造函数

示例:

typescript
class User extends ObjectType {
  name = String                    // 必需
  bio = Optional(String)           // string | undefined
}

// 生成的 TypeScript 类型
type UserType = {
  name: string
  bio?: string  // 会自动映射为可选属性
}

与 Nullable 的区别:

  • Optional(String) → 可以省略字段,或提供 undefined
  • Nullable(String) → 必须提供字段,值可以是 null

Nullable - 可空类型 ​

构造函数:

typescript
const Nullable = <T extends SchemaCtorInput>(Item: T): new () => NullableType

类型:

typescript
abstract class NullableType extends Schema {
  __type!: TypeOf<this['Item']> | null
  abstract Item: SchemaCtor
}

参数:

  • Item - 可空值的 Schema 类型

返回: 类型为 TypeOf<Item> | null 的 Nullable Schema 构造函数

示例:

typescript
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 }     // ✅ 显式提供 null

Record - 键值对类型 ​

构造函数:

typescript
const Record = <T extends SchemaCtorInput>(Item: T): new () => RecordType

类型:

typescript
abstract class RecordType extends Schema {
  __type!: { [key: string]: TypeOf<this['Item']> }
  abstract Item: SchemaCtor
}

参数:

  • Item - 值的 Schema 类型

返回: 类型为 { [key: string]: TypeOf<Item> } 的 Record Schema 构造函数

示例:

typescript
class Config extends ObjectType {
  labels = Record(String)          // { [key: string]: string }
  counters = Record(Number)        // { [key: string]: number }
}

Tuple - 元组类型 ​

构造函数:

typescript
const Tuple = <T extends SchemaCtorInput[]>(...Items: T): new () => TupleType

类型:

typescript
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 构造函数

示例:

typescript
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 - 对象类型 ​

类型:

typescript
abstract class ObjectType extends Schema {
  __type!: {
    [key in keyof this as SchemaField<this, key>]: TypeOfField<this[key]>
  }
}

说明:

  • 用于定义复杂的嵌套对象结构
  • 支持递归引用 - 这是 ObjectType 的核心优势
  • 字段可以是 Schema 构造函数、FieldInfo 或嵌套的 FieldDescriptors
  • 自动提取所有非 __type 的字段

基础用法:

typescript
class User extends ObjectType {
  id = String
  name = String
  age = Number
}

type UserType = TypeOf<typeof User>
// { id: string, name: string, age: number }

嵌套对象:

typescript
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
//     }
//   }
// }

递归引用(自引用):

typescript
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          // 递归可选类型
// }

相互引用:

typescript
class Post extends ObjectType {
  title = String
  author = User                    // 引用 User
}

class User extends ObjectType {
  name = String
  posts = List(Post)               // 引用 Post,形成循环引用
}

字段元数据:

typescript
import { field } from 'farrow-schema'

class User extends ObjectType {
  id = ID
  name = field({
    __type: String,
    description: '用户姓名',
    deprecated: '请使用 fullName'
  })
  age = Number
}

StructType - 结构体类型 ​

类型:

typescript
abstract class StructType extends Schema {
  __type!: ShallowPrettier<TypeOfFieldDescriptors<this['descriptors']>>
  abstract descriptors: FieldDescriptors
}

构造函数:

typescript
const Struct = <T extends FieldDescriptors>(descriptors: T): new () => StructType

说明:

  • 用于快速定义静态结构
  • 不支持递归引用 - 这是 Struct 的核心限制
  • 适合在联合类型中快速定义变体结构
  • 适合运行时动态构建 Schema

基础用法:

typescript
const UserStruct = Struct({
  id: String,
  name: String,
  age: Number
})

type UserType = TypeOf<typeof UserStruct>
// { id: string, name: string, age: number }

在联合类型中使用(推荐):

typescript
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 }

运行时动态构建:

typescript
function createValidator(fields: string[]) {
  const descriptors: FieldDescriptors = {}
  fields.forEach(field => {
    descriptors[field] = String
  })
  return Struct(descriptors)
}

const DynamicSchema = createValidator(['name', 'email', 'phone'])

❌ 不支持递归引用:

typescript
// ❌ 错误:不支持递归引用
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 选择指南:

特性ObjectTypeStruct
递归引用✅ 支持❌ 不支持
定义方式class 继承函数调用
适用场景复杂业务模型、树形结构联合类型变体、动态构建
性能稍慢(需实例化)稍快
推荐度通用推荐特定场景

联合与交集类型 ​

Union - 联合类型 ​

构造函数:

typescript
const Union = <T extends SchemaCtorInput[]>(...Items: T): new () => UnionType

类型:

typescript
abstract class UnionType extends Schema {
  __type!: TypeOf<this['Items'][number]>
  abstract Items: SchemaCtor[]
}

参数:

  • ...Items - 联合类型的成员 Schema 数组

返回: 联合类型 Schema 构造函数

枚举值:

typescript
const Status = Union(
  Literal('draft'),
  Literal('published'),
  Literal('archived')
)

type StatusType = TypeOf<typeof Status>
// 'draft' | 'published' | 'archived'

判别联合类型(推荐):

typescript
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 - 交集类型 ​

构造函数:

typescript
const Intersect = <T extends SchemaCtorInput[]>(...Items: T): new () => IntersectType

类型:

typescript
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 构造函数

示例:

typescript
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 - 字面量类型 ​

构造函数:

typescript
const Literal = <T extends Literals>(value: T): new () => LiteralType

类型:

typescript
type Literals = number | string | boolean | null | undefined

abstract class LiteralType extends Schema {
  __type!: this['value']
  abstract value: Literals
}

参数:

  • value - 字面量值

返回: 字面量类型 Schema 构造函数

示例:

typescript
const Environment = Union(
  Literal('development'),
  Literal('staging'),
  Literal('production')
)

const APIResponse = Struct({
  status: Literal(200),
  message: Literal('OK'),
  data: Any
})

预定义字面量:

typescript
const Null = Literal(null)
const Undefined = Literal(undefined)

特殊类型 Schema ​

Any - 任意类型 ​

typescript
class Any extends Schema {
  __type!: any
}

对应 TypeScript 类型: any验证: 接受任何值


Unknown - 未知类型 ​

typescript
class Unknown extends Schema {
  __type!: unknown
}

对应 TypeScript 类型: unknown验证: 接受任何值,但类型更安全


Never - 永不类型 ​

typescript
class Never extends Schema {
  __type!: never
}

对应 TypeScript 类型: never验证: 不应该验证此类型,会抛出错误


Json - JSON 类型 ​

typescript
type JsonType =
  | number
  | string
  | boolean
  | null
  | undefined
  | JsonType[]
  | { toJSON(): string }
  | { [key: string]: JsonType }

class Json extends Schema {
  __type!: JsonType
}

对应 TypeScript 类型: JsonType (递归 JSON 类型) 验证: 验证值是否为合法 JSON 值


修饰符类型 ​

Strict - 严格模式 ​

构造函数:

typescript
const Strict = <T extends SchemaCtorInput>(Item: T): new () => StrictType

类型:

typescript
abstract class StrictType extends Schema {
  __type!: TypeOf<this['Item']>
  abstract Item: SchemaCtor
}

说明: 包裹的 Schema 在验证时强制使用严格模式

示例:

typescript
class User extends ObjectType {
  age = Strict(Number)  // 必须是纯数字,不接受 "25" 字符串
}

NonStrict - 非严格模式 ​

构造函数:

typescript
const NonStrict = <T extends SchemaCtorInput>(Item: T): new () => NonStrictType

类型:

typescript
abstract class NonStrictType extends Schema {
  __type!: TypeOf<this['Item']>
  abstract Item: SchemaCtor
}

说明: 包裹的 Schema 在验证时强制使用非严格模式

示例:

typescript
class User extends ObjectType {
  age = NonStrict(Number)  // 接受 25 或 "25"
}

ReadOnly - 只读类型 ​

构造函数:

typescript
const ReadOnly = <T extends SchemaCtorInput>(Item: T): new () => ReadOnlyType

类型:

typescript
abstract class ReadOnlyType extends Schema {
  __type!: Readonly<TypeOf<this['Item']>>
  abstract Item: SchemaCtor
}

说明: 浅层只读,仅顶层属性只读


ReadOnlyDeep - 深度只读类型 ​

构造函数:

typescript
const ReadOnlyDeep = <T extends SchemaCtorInput>(Item: T): new () => ReadOnlyDeepType

类型:

typescript
abstract class ReadOnlyDeepType extends Schema {
  __type!: MarkReadOnlyDeep<TypeOf<this['Item']>>
  abstract Item: SchemaCtor
}

说明: 深层递归只读,所有嵌套属性都只读


类型工具函数 ​

TypeOf - 提取 TypeScript 类型 ​

类型签名:

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 类型

示例:

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 构造函数 ​

函数签名:

typescript
const getSchemaCtor = <T extends SchemaCtor>(Ctor: T): SchemaTypeOf<T>

说明: 将原始类型构造函数(Number、String 等)转换为对应的 Schema 类

示例:

typescript
const StringSchema = getSchemaCtor(String)     // 返回 farrow-schema 的 String 类
const NumberSchema = getSchemaCtor(Number)     // 返回 farrow-schema 的 Number 类
const UserSchema = getSchemaCtor(User)         // 返回 User 类本身

getInstance - 获取 Schema 实例 ​

函数签名:

typescript
const getInstance = <T extends SchemaCtor>(Ctor: T): InstanceTypeOf<T>

核心作用: 将 Schema 构造函数转换为运行时实例,提取字段类型信息。采用单例模式(WeakMap 缓存),每个构造函数只实例化一次。

为什么需要:

  • Schema 类的字段定义(如 name = String)只存在于实例上
  • 构造函数本身无法访问这些字段信息
  • 验证器和格式化器需要遍历字段进行处理

示例:

typescript
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

内部使用:

typescript
// 验证器中
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 构造函数 ​

函数签名:

typescript
const toSchemaCtor = <T extends SchemaCtorInput>(Item: T): ToSchemaCtor<T>

说明:

  • 如果输入是 Schema 构造函数,直接返回
  • 如果输入是 FieldDescriptors 对象,自动包裹为 Struct

示例:

typescript
const StringCtor = toSchemaCtor(String)
// 返回 String Schema

const StructCtor = toSchemaCtor({
  name: String,
  age: Number
})
// 返回 Struct({ name: String, age: Number })

isSchemaCtor - 判断是否是 Schema 构造函数 ​

函数签名:

typescript
const isSchemaCtor = (input: any): input is SchemaCtor

说明: 类型守卫,判断输入是否为 Schema 构造函数


验证系统 (Validator) ​

导入路径: farrow-schema/validator

Validator 对象 ​

验证系统的核心对象,提供验证功能。

类型:

typescript
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 - 验证数据 ​

函数签名:

typescript
function validate<T extends SchemaCtor>(
  Ctor: T,
  input: unknown,
  options?: ValidatorOptions
): ValidationResult<TypeOf<T>>

参数:

  • Ctor - Schema 构造函数
  • input - 待验证的数据
  • options - 验证选项
    • strict?: boolean - 是否严格模式(默认 false)

返回: Result<T, ValidationError> 类型的验证结果

示例:

typescript
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 - 实现自定义验证器 ​

函数签名:

typescript
function impl<T extends Schema>(
  Ctor: abstract new () => T,
  impl: ValidatorImpl<T>
): void

参数:

  • Ctor - Schema 构造函数
  • impl - 验证器实现,可以是对象或工厂函数

说明: 为 Schema 类型注册验证器实现(内部使用 WeakMap 存储)

示例:

typescript
// 对象形式
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 - 获取验证器实现 ​

函数签名:

typescript
function get<T extends SchemaCtor>(
  Ctor: T
): ValidatorMethods<SchemaTypeOf<T>> | undefined

参数:

  • Ctor - Schema 构造函数

返回: 验证器方法对象或 undefined

说明: 获取 Schema 对应的验证器实现(沿原型链查找)


ValidationResult 类型 ​

类型定义:

typescript
type ValidationResult<T = any> = Result<T, ValidationError>

type ValidationError = {
  path?: (string | number)[]
  message: string
}

说明:

  • path - 错误路径,数组形式,如 ['user', 'profile', 'age']
  • message - 错误消息

示例:

typescript
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 类型 ​

类型定义:

typescript
type ValidatorOptions = {
  strict?: boolean
}

说明:

  • strict: true - 严格模式(默认),不进行类型转换
    • "25" 不会转换为 25
    • "true" 不会转换为 true
  • strict: false - 宽松模式,尝试类型转换
    • "25" → 25
    • "true" → true
    • "2024-01-01" → Date

createSchemaValidator - 创建专用验证器 ​

函数签名:

typescript
function createSchemaValidator<S extends SchemaCtor>(
  SchemaCtor: S,
  options?: ValidatorOptions
): (input: unknown) => ValidationResult<TypeOf<S>>

参数:

  • SchemaCtor - Schema 构造函数
  • options - 验证选项

返回: 绑定了 Schema 和选项的验证函数

示例:

typescript
// 创建专用验证器
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 - 自定义验证器基类 ​

类型定义:

typescript
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>
}

说明: 用于创建自定义验证器的抽象基类

示例:

typescript
// 邮箱验证器
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 - 正则表达式验证器 ​

函数签名:

typescript
const RegExp = (regexp: RegExp): new () => ValidatorType<string>

参数:

  • regexp - 正则表达式

返回: 正则验证器构造函数

示例:

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}$/)
}

格式化系统 (Formatter) ​

导入路径: farrow-schema/formatter

格式化系统用于将 Schema 转换为结构化的类型元数据,常用于:

  • 生成 API 文档
  • 生成客户端代码
  • 类型信息序列化
  • GraphQL Schema 生成

Formatter 对象 ​

格式化系统的核心对象。

类型:

typescript
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 ​

函数签名:

typescript
function format<T extends SchemaCtor>(
  Ctor: T,
  context?: FormatContext
): {
  typeId: number
  types: FormatTypes
}

type FormatTypes = {
  [key: string]: FormatType
}

参数:

  • Ctor - Schema 构造函数
  • context - 格式化上下文(可选)

返回:

  • typeId - 根类型的 ID
  • types - 类型字典,键为类型 ID(字符串形式),值为格式化类型

示例:

typescript
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" }
}
*/

复杂示例:

typescript
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 的别名 ​

函数签名:

typescript
const formatSchema = Formatter.format

说明: Formatter.format 的导出别名

示例:

typescript
import { formatSchema } from 'farrow-schema/formatter'

const result = formatSchema(User)

Formatter.formatSchema - 格式化 Schema(上下文版本) ​

函数签名:

typescript
function formatSchema<T extends SchemaCtor>(
  Ctor: T,
  ctx: FormatContext
): number

参数:

  • Ctor - Schema 构造函数
  • ctx - 格式化上下文(必需)

返回: 类型 ID

说明:

  • 在给定上下文中格式化 Schema
  • 使用上下文的 addType 和 formatCache
  • 用于在自定义格式化器中格式化嵌套类型

Formatter.impl - 实现自定义格式化器 ​

函数签名:

typescript
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 类型注册格式化器实现

示例:

typescript
// 对象形式
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 - 获取格式化器实现 ​

函数签名:

typescript
function get<T extends SchemaCtor>(
  Ctor: T
): FormatterMethods | undefined

参数:

  • Ctor - Schema 构造函数

返回: 格式化器方法对象或 undefined

说明: 获取 Schema 对应的格式化器实现(沿原型链查找)


FormatContext - 格式化上下文 ​

类型定义:

typescript
type FormatContext = {
  addType: (type: FormatType) => number
  formatCache: WeakMap<Function, number>
}

说明:

  • addType - 添加类型到类型字典,返回分配的类型 ID
  • formatCache - 类型缓存,避免重复格式化同一类型

使用场景: 在自定义格式化器中使用


FormatType - 格式化类型(联合类型) ​

类型定义:

typescript
type FormatType =
  | FormatScalarType
  | FormatObjectType
  | FormatUnionType
  | FormatStructType
  | FormatRecordType
  | FormatListType
  | FormatLiteralType
  | FormatNullableType
  | FormatOptionalType
  | FormatIntersectType
  | FormatTupleType
  | FormatStrictType
  | FormatNonStrictType
  | FormatReadOnlyType
  | FormatReadonlyDeepType

说明: 所有格式化类型的联合,每种类型都有 type 判别字段


格式化类型详解 ​

FormatScalarType - 标量类型格式 ​

类型定义:

typescript
type FormatScalarType = {
  type: 'Scalar'
  valueType: string    // TypeScript 类型字符串
  valueName: string    // Schema 名称
}

对应 Schema: String, Number, Int, Float, Boolean, ID, Date, Any, Unknown, Never, Json

示例:

typescript
// 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 - 对象类型格式 ​

类型定义:

typescript
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 的类名或 displayName
  • fields - 对象字段字典
    • 键为字段名
    • 值为字段格式信息
  • namespace - 命名空间(如果设置)

示例:

typescript
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 - 结构体类型格式 ​

类型定义:

typescript
type FormatStructType = {
  type: 'Struct'
  name?: string
  fields: FormatFields
  namespace?: string
}

对应 Schema: StructType

说明: 与 FormatObjectType 类似,但 name 是可选的

示例:

typescript
const UserStruct = Struct({
  id: ID,
  name: String
})

const { types } = Formatter.format(UserStruct)
/*
types["0"] = {
  type: "Struct",
  name: undefined,  // Struct 默认没有名称
  fields: { ... }
}
*/

FormatListType - 列表类型格式 ​

类型定义:

typescript
type FormatListType = {
  type: 'List'
  itemTypeId: number
  $ref: string
}

对应 Schema: List

字段说明:

  • itemTypeId - 列表元素类型的 ID
  • $ref - 元素类型的引用路径

示例:

typescript
class Blog extends ObjectType {
  tags = List(String)
}

const { types } = Formatter.format(Blog)
/*
types["2"] = {
  type: "List",
  itemTypeId: 1,      // String 的 typeId
  $ref: "#/types/1"
}
*/

FormatNullableType - 可空类型格式 ​

类型定义:

typescript
type FormatNullableType = {
  type: 'Nullable'
  itemTypeId: number
  $ref: string
}

对应 Schema: Nullable


FormatOptionalType - 可选类型格式 ​

类型定义:

typescript
type FormatOptionalType = {
  type: 'Optional'
  itemTypeId: number
  $ref: string
}

对应 Schema: Optional


FormatLiteralType - 字面量类型格式 ​

类型定义:

typescript
type FormatLiteralType = {
  type: 'Literal'
  value: Literals
}

type Literals = number | string | boolean | null | undefined

对应 Schema: Literal

示例:

typescript
const Status = Literal('active')

const { types } = Formatter.format(Status)
/*
types["0"] = {
  type: "Literal",
  value: "active"
}
*/

FormatUnionType - 联合类型格式 ​

类型定义:

typescript
type FormatUnionType = {
  type: 'Union'
  name?: string
  itemTypes: { typeId: number; $ref: string }[]
  namespace?: string
}

对应 Schema: Union

字段说明:

  • itemTypes - 联合类型成员数组
    • 每个成员包含 typeId 和 $ref

示例:

typescript
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 - 交集类型格式 ​

类型定义:

typescript
type FormatIntersectType = {
  type: 'Intersect'
  name?: string
  itemTypes: { typeId: number; $ref: string }[]
  namespace?: string
}

对应 Schema: Intersect


FormatTupleType - 元组类型格式 ​

类型定义:

typescript
type FormatTupleType = {
  type: 'Tuple'
  name?: string
  itemTypes: { typeId: number; $ref: string }[]
  namespace?: string
}

对应 Schema: Tuple

示例:

typescript
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 类型格式 ​

类型定义:

typescript
type FormatRecordType = {
  type: 'Record'
  valueTypeId: number
  $ref: string
}

对应 Schema: Record


FormatStrictType - 严格类型格式 ​

类型定义:

typescript
type FormatStrictType = {
  type: 'Strict'
  itemTypeId: number
  $ref: string
}

对应 Schema: Strict


FormatNonStrictType - 非严格类型格式 ​

类型定义:

typescript
type FormatNonStrictType = {
  type: 'NonStrict'
  itemTypeId: number
  $ref: string
}

对应 Schema: NonStrict


FormatReadOnlyType - 只读类型格式 ​

类型定义:

typescript
type FormatReadOnlyType = {
  type: 'ReadOnly'
  itemTypeId: number
  $ref: string
}

对应 Schema: ReadOnly


FormatReadonlyDeepType - 深度只读类型格式 ​

类型定义:

typescript
type FormatReadonlyDeepType = {
  type: 'ReadOnlyDeep'
  itemTypeId: number
  $ref: string
}

对应 Schema: ReadOnlyDeep


isNamedFormatType - 判断是否是命名格式类型 ​

函数签名:

typescript
const isNamedFormatType = (input: FormatType): input is NamedFormatType

type NamedFormatType =
  | FormatTupleType
  | FormatStructType
  | FormatUnionType
  | FormatIntersectType
  | FormatObjectType

说明: 类型守卫,判断格式类型是否可以有名称

示例:

typescript
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 文档 ​

typescript
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 - 字段定义辅助函数 ​

函数签名:

typescript
const field = <T extends FieldInfo>(fieldInfo: T): T

type FieldInfo = {
  __type: SchemaCtor
  description?: string
  deprecated?: string
}

参数:

  • fieldInfo - 字段信息对象
    • __type - 字段的 Schema 类型
    • description - 字段描述(可选)
    • deprecated - 弃用说明(可选)

返回: 原样返回 fieldInfo(用于类型推导)

示例:

typescript
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 字段 ​

函数签名:

typescript
const pickObject = <T extends ObjectType, Keys extends SchemaField<T, keyof T>[]>(
  Ctor: new () => T,
  keys: Keys
): PickObject<T, Keys>

参数:

  • Ctor - ObjectType 构造函数
  • keys - 要选择的字段名数组

返回: 新的 ObjectType 构造函数,仅包含选择的字段

示例:

typescript
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 字段 ​

函数签名:

typescript
const pickStruct = <T extends StructType, Keys extends (keyof T['descriptors'])[]>(
  Ctor: new () => T,
  keys: Keys
): new () => StructType

参数:

  • Ctor - StructType 构造函数
  • keys - 要选择的字段名数组

返回: 新的 StructType 构造函数,仅包含选择的字段


pick - 通用选择函数 ​

函数签名:

typescript
const pick: typeof pickObject & typeof pickStruct

说明: 自动判断是 ObjectType 还是 StructType,调用对应函数

⚠️ 类型推导限制: 由于 TypeScript 的限制,pick 通用函数在输入第二个参数(字段名数组)时无法提供准确的类型提示和自动补全。

推荐做法:

  • 对于 ObjectType,使用 pickObject
  • 对于 StructType,使用 pickStruct

这样可以获得完整的类型推导和 IDE 智能提示。

示例:

typescript
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 字段 ​

函数签名:

typescript
const omitObject = <T extends ObjectType, Keys extends SchemaField<T, keyof T>[]>(
  Ctor: new () => T,
  keys: Keys
): OmitObject<T, Keys>

参数:

  • Ctor - ObjectType 构造函数
  • keys - 要排除的字段名数组

返回: 新的 ObjectType 构造函数,不包含排除的字段

示例:

typescript
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 字段 ​

函数签名:

typescript
const omitStruct = <T extends StructType, Keys extends (keyof T['descriptors'])[]>(
  Ctor: new () => T,
  keys: Keys
): new () => StructType

omit - 通用排除函数 ​

函数签名:

typescript
const omit: typeof omitObject & typeof omitStruct

说明: 自动判断是 ObjectType 还是 StructType,调用对应函数

⚠️ 类型推导限制: 由于 TypeScript 的限制,omit 通用函数在输入第二个参数(字段名数组)时无法提供准确的类型提示和自动补全。

推荐做法:

  • 对于 ObjectType,使用 omitObject
  • 对于 StructType,使用 omitStruct

这样可以获得完整的类型推导和 IDE 智能提示。

示例:

typescript
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 可选化 ​

函数签名:

typescript
const partialObject = <T extends ObjectType>(
  Ctor: new () => T
): PartialObjectType<T>

参数:

  • Ctor - ObjectType 构造函数

返回: 新的 ObjectType 构造函数,所有字段变为可选

示例:

typescript
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 可选化 ​

函数签名:

typescript
const partialStruct = <T extends StructType>(
  Ctor: new () => T
): new () => PartialType

partial - 通用可选化函数 ​

函数签名:

typescript
const partial: typeof partialObject & typeof partialStruct

示例:

typescript
import { partial } from 'farrow-schema'

const PartialUser = partial(User)
const PartialUserStruct = partial(UserStruct)

required 系列 - 转为必填字段 ​

requiredObject - ObjectType 必填化 ​

函数签名:

typescript
const requiredObject = <T extends ObjectType>(
  Ctor: new () => T
): RequiredObjectType<T>

参数:

  • Ctor - ObjectType 构造函数

返回: 新的 ObjectType 构造函数,所有可选字段变为必填

示例:

typescript
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 必填化 ​

函数签名:

typescript
const requiredStruct = <T extends StructType>(
  Ctor: new () => T
): new () => StructType

required - 通用必填化函数 ​

函数签名:

typescript
const required: typeof requiredObject & typeof requiredStruct

示例:

typescript
import { required } from 'farrow-schema'

const RequiredUser = required(OptionalUser)

keyof 系列 - 获取字段键 ​

keyofObject - 获取 ObjectType 字段键 ​

函数签名:

typescript
const keyofObject = <T extends ObjectType>(
  Ctor: new () => T
): (keyof TypeOf<T>)[]

参数:

  • Ctor - ObjectType 构造函数

返回: 字段名数组

示例:

typescript
import { keyofObject } from 'farrow-schema'

class User extends ObjectType {
  id = String
  name = String
  email = String
}

const keys = keyofObject(User)
// ['id', 'name', 'email']

keyofStruct - 获取 Struct 字段键 ​

函数签名:

typescript
const keyofStruct = <T extends StructType>(
  Ctor: new () => T
): (keyof T['descriptors'])[]

keyof - 通用获取键函数 ​

函数签名:

typescript
const keyof: typeof keyofObject & typeof keyofStruct

示例:

typescript
import { keyof } from 'farrow-schema'

const userKeys = keyof(User)
const structKeys = keyof(UserStruct)

Result 类型 ​

导入路径: farrow-schema(主入口自动导出)

Result 类型提供函数式错误处理模式。


Result 类型定义 ​

类型:

typescript
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 - 创建成功结果 ​

函数签名:

typescript
const Ok = <T, E = string>(value: T): Result<T, E>

参数:

  • value - 成功值

返回: Ok 结果

示例:

typescript
import { Ok } from 'farrow-schema'

const result = Ok(42)
// { kind: 'Ok', value: 42, isOk: true, isErr: false }

Err - 创建错误结果 ​

函数签名:

typescript
const Err = <E = string>(value: E): Err<E>

参数:

  • value - 错误值

返回: Err 结果

示例:

typescript
import { Err } from 'farrow-schema'

const result = Err('Something went wrong')
// { kind: 'Err', value: 'Something went wrong', isOk: false, isErr: true }

Result 使用模式 ​

类型收窄(推荐):

typescript
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
}

业务逻辑中使用:

typescript
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 ​

类型定义:

typescript
type DateInstanceType = InstanceType<typeof Date>

说明: Date 实例类型的别名,等同于 Date


MarkReadOnlyDeep ​

类型定义:

typescript
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

说明: 深度递归只读类型工具

示例:

typescript
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 定义业务模型 ​

typescript
// 推荐:清晰的业务模型
class User extends ObjectType {
  id = ID
  name = String
  email = String
  profile = {
    bio: String,
    avatar: Optional(String)
  }
}

// 不推荐:使用 Struct(不支持递归)
const User = Struct({
  id: ID,
  name: String
})

✅ 推荐:使用字段元数据 ​

typescript
class User extends ObjectType {
  id = ID

  email = field({
    __type: String,
    description: '用户邮箱地址',
    deprecated: '请使用 contactEmail'
  })
}

2. 验证模式 ​

✅ 推荐:创建专用验证器 ​

typescript
// 推荐:提前创建验证器,复用
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 类型进行错误处理 ​

typescript
// 推荐:使用 kind 字段收窄
if (result.kind === 'Ok') {
  console.log(result.value)
} else {
  console.log(result.value.message)
}

3. 自定义验证器模式 ​

✅ 推荐:继承 ValidatorType ​

typescript
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. 类型转换模式 ​

✅ 推荐:使用辅助函数进行类型派生 ​

typescript
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(支持递归) ​

typescript
// 树形结构
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. 联合类型模式 ​

✅ 推荐:使用判别联合类型 ​

typescript
// 推荐:使用 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 文档 ​

typescript
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) → 字段可以省略或为 undefined
  • Nullable(String) → 字段必须提供,值可以是 null
typescript
class User extends ObjectType {
  bio = Optional(String)      // 可以省略
  deletedAt = Nullable(Date)  // 必须提供,可以为 null
}

// ✅ 合法
{ bio: undefined }
{ deletedAt: null }

// ❌ 非法
{ bio: null }        // bio 不接受 null
{ }                  // deletedAt 字段缺失

Q: 如何提取嵌套类型? ​

A: 使用 TypeOf 结合 TypeScript 的索引访问:

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 | undefined

Q: 如何派生多个变体类型? ​

A: 使用辅助函数链式组合:

typescript
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: 格式化器输出可用于多种场景:

typescript
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 能够帮你构建类型安全、可维护的应用程序。

这是一个第三方 Farrow 文档站 | 用 ❤️ 和 TypeScript 构建