Schema Type System
farrow-schema provides a complete type system that allows you to precisely describe data structures.
🧱 Basic Types
Scalar Types
typescript
import { String, Number, Boolean, Date, ID } from 'farrow-schema'
class Profile extends ObjectType {
id = ID // string (non-empty)
name = String // string
age = Number // number
active = Boolean // boolean
createdAt = Date // Date
}Numeric Types
typescript
class Product extends ObjectType {
price = Number // Any number
quantity = Int // Integer (checked during validation)
rating = Float // Float
}Optional and Nullable
typescript
import { Optional, Nullable } from 'farrow-schema'
class Article extends ObjectType {
title = String
bio = Optional(String) // string | undefined (field can be omitted)
content = Nullable(String) // string | null (field must be provided)
}
// Usage examples
const data1 = { title: 'Hello' } // ✅ bio can be omitted
const data2 = { title: 'Hello', bio: undefined } // ✅
const data3 = { title: 'Hello', content: null } // ✅ content explicitly nullOptional vs Nullable:
Optional(T)→T | undefined, field can be omittedNullable(T)→T | null, field must exist but value can be null
Composite Types
List Types
typescript
import { List } from 'farrow-schema'
class Blog extends ObjectType {
tags = List(String) // string[]
scores = List(Number) // number[]
comments = List(Comment) // Comment[] (nested objects)
}Record Types
typescript
import { Record } from 'farrow-schema'
class Config extends ObjectType {
labels = Record(String) // { [key: string]: string }
counters = Record(Number) // { [key: string]: number }
}Tuple Types
typescript
import { Tuple } from 'farrow-schema'
class Geometry extends ObjectType {
point = Tuple(Number, Number) // [number, number]
rgb = Tuple(Number, Number, Number) // [number, number, number]
mixed = Tuple(String, Number, Boolean) // [string, number, boolean]
}Object Types
Basic Objects
typescript
class User extends ObjectType {
id = String
name = String
email = String
}
type UserType = TypeOf<typeof User>
// {
// id: string
// name: string
// email: string
// }Nested Objects
Use object literals to define nested structures:
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
// }
// }
// }Recursive References
Core advantage of ObjectType: supports self-references and mutual references
typescript
// Self-reference - tree structure
class Comment extends ObjectType {
id = String
content = String
replies = List(Comment) // ✅ Supports self-reference
parent = Optional(Comment) // ✅ Optional self-reference
}
// Mutual references
class Post extends ObjectType {
title = String
author = User // Reference User
}
class User extends ObjectType {
name = String
posts = List(Post) // Reference Post, forming circular reference
}Struct - Quick Structure Definition
Applicable scenarios:
- Define variants in union types
- Dynamically build Schema at runtime
- Quickly define simple structures
Limitation: Does not support recursive references
typescript
import { Struct } from 'farrow-schema'
// Basic usage
const UserStruct = Struct({
id: String,
name: String,
age: Number
})
type UserType = TypeOf<typeof UserStruct>
// { id: string, name: string, age: number }Use in union types (recommended scenario):
typescript
import { Union, Literal } from 'farrow-schema'
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 }ObjectType vs Struct:
| Feature | ObjectType | Struct |
|---|---|---|
| Definition method | Class inheritance | Function call |
| Recursive references | ✅ Supported | ❌ Not supported |
| Applicable scenarios | Complex business models, tree structures | Union type variants, dynamic building |
| Recommendation | General recommendation | Specific scenarios |
Union and Literal Types
Literal Types
typescript
import { Literal } from 'farrow-schema'
// Single literal
const StatusActive = Literal('active')
const HttpOK = Literal(200)Union Types
typescript
import { Union } from 'farrow-schema'
// Enumeration values
const Status = Union(
Literal('draft'),
Literal('published'),
Literal('archived')
)
type StatusType = TypeOf<typeof Status>
// 'draft' | 'published' | 'archived'Discriminated Unions (Recommended Pattern)
Use type field as discriminator:
typescript
const Payment = Union(
Struct({
type: Literal('credit_card'),
cardNumber: String,
cvv: String
}),
Struct({
type: Literal('paypal'),
email: String
}),
Struct({
type: Literal('crypto'),
wallet: String,
network: String
})
)
type PaymentType = TypeOf<typeof Payment>
// TypeScript automatic type narrowing
function processPayment(payment: PaymentType) {
switch (payment.type) {
case 'credit_card':
console.log(payment.cardNumber) // ✅ Type safe
break
case 'paypal':
console.log(payment.email) // ✅ Type safe
break
case 'crypto':
console.log(payment.wallet) // ✅ Type safe
break
}
}