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
  • 使用上下文的 addTypeformatCache
  • 用于在自定义格式化器中格式化嵌套类型

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 构建