Skip to content

Schema 高级操作

farrow-schema 提供了丰富的 Schema 操作工具,让你能够灵活地派生和组合类型。

字段元数据

使用 field 函数添加字段描述:

typescript
import { field } from 'farrow-schema'

class User extends ObjectType {
  id = ID

  name = field({
    __type: String,
    description: '用户姓名'
  })

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

Schema 操作工具

pick - 选择字段

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 }

omit - 排除字段

typescript
import { omitObject } from 'farrow-schema'

// 排除敏感字段
const SafeUser = omitObject(FullUser, ['password'])

type SafeUserType = TypeOf<typeof SafeUser>
// { id: string, name: string, email: string, createdAt: Date }

// 创建输入类型
const CreateUserInput = omitObject(FullUser, ['id', 'createdAt'])

type CreateUserInputType = TypeOf<typeof CreateUserInput>
// { name: string, email: string, password: string }

partial - 转为可选

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 }

// 实际应用:更新输入
const UpdateUserInput = partialObject(
  omitObject(FullUser, ['id', 'createdAt'])
)
// 所有字段都是可选的,用于 PATCH 请求

required - 转为必填

typescript
import { requiredObject } from 'farrow-schema'

class OptionalUser extends ObjectType {
  id = Optional(String)
  name = Optional(String)
}

const RequiredUser = requiredObject(OptionalUser)

type RequiredUserType = TypeOf<typeof RequiredUser>
// { id: string, name: string }

keyof - 获取字段键

typescript
import { keyofObject } from 'farrow-schema'

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

💡 提示:所有工具函数都有 ObjectStruct 专用版本:

  • pickObject / pickStruct
  • omitObject / omitStruct
  • partialObject / partialStruct
  • requiredObject / requiredStruct
  • keyofObject / keyofStruct

推荐使用专用版本以获得更好的类型提示。

交集类型

typescript
import { Intersect } from 'farrow-schema'

class BaseUser extends ObjectType {
  id = String
  name = String
}

class ContactInfo extends ObjectType {
  email = String
  phone = String
}

// 合并多个类型
const FullUser = Intersect(BaseUser, ContactInfo)

type FullUserType = TypeOf<typeof FullUser>
// { id: string, name: string, email: string, phone: string }

修饰符类型

Strict / NonStrict

在字段级别控制验证模式:

typescript
import { Strict, NonStrict } from 'farrow-schema'

class Product extends ObjectType {
  price = Strict(Number)      // 必须是纯数字
  quantity = NonStrict(Int)   // 接受 "42" 并转换为 42
}

ReadOnly / ReadOnlyDeep

typescript
import { ReadOnly, ReadOnlyDeep } from 'farrow-schema'

class Config extends ObjectType {
  settings = ReadOnly(Record(String))      // 浅层只读
  nested = ReadOnlyDeep(ComplexObject)     // 深层只读
}

元数据提取

基础用法

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 (根类型的 ID)
console.log(types)
/*
{
  "0": {
    type: "Object",
    name: "User",
    fields: {
      id: { typeId: 1, $ref: "#/types/1" },
      name: { typeId: 2, $ref: "#/types/2" },
      age: { typeId: 3, $ref: "#/types/3" },
      tags: { typeId: 4, $ref: "#/types/4" }
    }
  },
  "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" }
}
*/

应用场景

生成 API 文档

typescript
import { Formatter, isNamedFormatType } from 'farrow-schema/formatter'

class CreateUserInput extends ObjectType {
  name = field({
    __type: String,
    description: '用户姓名'
  })
  email = field({
    __type: String,
    description: '用户邮箱'
  })
}

const { typeId, types } = Formatter.format(CreateUserInput)

// 遍历类型生成文档
for (const [id, formatType] of Object.entries(types)) {
  if (formatType.type === 'Object') {
    console.log(`## ${formatType.name}`)

    for (const [fieldName, field] of Object.entries(formatType.fields)) {
      console.log(`- **${fieldName}**: ${field.description || ''}`)
    }
  }
}

生成客户端类型

typescript
function generateTypeScript(types: FormatTypes): string {
  let code = ''

  for (const [id, formatType] of Object.entries(types)) {
    if (formatType.type === 'Object') {
      code += `export interface ${formatType.name} {\n`

      for (const [fieldName, field] of Object.entries(formatType.fields)) {
        const fieldType = getTypeScriptType(types[field.typeId])
        code += `  ${fieldName}: ${fieldType}\n`
      }

      code += `}\n\n`
    }
  }

  return code
}

组合操作示例

typescript
// 定义完整模型
class FullUser extends ObjectType {
  id = String
  name = String
  email = String
  password = String
  role = String
  createdAt = Date
  updatedAt = Date
}

// 派生多个 Schema
const PublicUser = pickObject(FullUser, ['id', 'name'])
const CreateUserInput = omitObject(FullUser, ['id', 'createdAt', 'updatedAt'])
const UpdateUserInput = partialObject(CreateUserInput)
const AdminUser = FullUser  // 管理员可以看到完整信息

// 类型推导
type PublicUserType = TypeOf<typeof PublicUser>
// { id: string, name: string }

type CreateUserInputType = TypeOf<typeof CreateUserInput>
// { name: string, email: string, password: string, role: string }

type UpdateUserInputType = TypeOf<typeof UpdateUserInput>
// { name?: string, email?: string, password?: string, role?: string }

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