Advanced Schema Operations
farrow-schema provides rich Schema manipulation tools, allowing you to flexibly derive and combine types.
Field Metadata
Use field function to add field descriptions:
typescript
import { field } from 'farrow-schema'
class User extends ObjectType {
id = ID
name = field({
__type: String,
description: 'User name'
})
email = field({
__type: String,
description: 'User email',
deprecated: 'Please use contactEmail'
})
}Schema Operation Tools
pick - Select Fields
typescript
import { pickObject } from 'farrow-schema'
class FullUser extends ObjectType {
id = String
name = String
email = String
password = String
createdAt = Date
}
// Select public fields
const PublicUser = pickObject(FullUser, ['id', 'name'])
type PublicUserType = TypeOf<typeof PublicUser>
// { id: string, name: string }omit - Exclude Fields
typescript
import { omitObject } from 'farrow-schema'
// Exclude sensitive fields
const SafeUser = omitObject(FullUser, ['password'])
type SafeUserType = TypeOf<typeof SafeUser>
// { id: string, name: string, email: string, createdAt: Date }
// Create input type
const CreateUserInput = omitObject(FullUser, ['id', 'createdAt'])
type CreateUserInputType = TypeOf<typeof CreateUserInput>
// { name: string, email: string, password: string }partial - Convert to Optional
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 }
// Practical application: update input
const UpdateUserInput = partialObject(
omitObject(FullUser, ['id', 'createdAt'])
)
// All fields are optional, used for PATCH requestsrequired - Convert to 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 - Get Field Keys
typescript
import { keyofObject } from 'farrow-schema'
const keys = keyofObject(User)
// ['id', 'name', 'email']💡 Tip: All tool functions have Object and Struct specific versions:
pickObject/pickStructomitObject/omitStructpartialObject/partialStructrequiredObject/requiredStructkeyofObject/keyofStruct
It's recommended to use specific versions for better type hints.
Intersection Types
typescript
import { Intersect } from 'farrow-schema'
class BaseUser extends ObjectType {
id = String
name = String
}
class ContactInfo extends ObjectType {
email = String
phone = String
}
// Merge multiple types
const FullUser = Intersect(BaseUser, ContactInfo)
type FullUserType = TypeOf<typeof FullUser>
// { id: string, name: string, email: string, phone: string }Modifier Types
Strict / NonStrict
Control validation mode at field level:
typescript
import { Strict, NonStrict } from 'farrow-schema'
class Product extends ObjectType {
price = Strict(Number) // Must be pure number
quantity = NonStrict(Int) // Accept "42" and convert to 42
}ReadOnly / ReadOnlyDeep
typescript
import { ReadOnly, ReadOnlyDeep } from 'farrow-schema'
class Config extends ObjectType {
settings = ReadOnly(Record(String)) // Shallow read-only
nested = ReadOnlyDeep(ComplexObject) // Deep read-only
}Metadata Extraction
Basic Usage
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 of root type)
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" }
}
*/Application Scenarios
Generate API Documentation
typescript
import { Formatter, isNamedFormatType } from 'farrow-schema/formatter'
class CreateUserInput extends ObjectType {
name = field({
__type: String,
description: 'User name'
})
email = field({
__type: String,
description: 'User email'
})
}
const { typeId, types } = Formatter.format(CreateUserInput)
// Traverse types to generate documentation
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 || ''}`)
}
}
}Generate Client Types
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
}Combination Operation Examples
typescript
// Define complete model
class FullUser extends ObjectType {
id = String
name = String
email = String
password = String
role = String
createdAt = Date
updatedAt = Date
}
// Derive multiple Schemas
const PublicUser = pickObject(FullUser, ['id', 'name'])
const CreateUserInput = omitObject(FullUser, ['id', 'createdAt', 'updatedAt'])
const UpdateUserInput = partialObject(CreateUserInput)
const AdminUser = FullUser // Admins can see complete information
// Type inference
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 }