札记

API 全表

litetype 的全部公共 API,逐个列出。叙事导览看 首页;这里是查字典用的平表。

npm i litetype

两个入口:

import { /* 下面全部核心 */ } from 'litetype'
import { fromJsonSchema, toJsonSchema } from 'litetype/jsonschema'

import 'litetype' 零运行时依赖、不拉 jsonschema;后者按需走子路径。


动词(外置,吃 schema + data)

函数

签名

行为

parse

parse(schema, data): Infer<S>

通过返回 typed data,失败 throw SchemaError

safeParse

safeParse(schema, data): { success, data } | { success, error }

不抛,返回判别结果

check

check(schema, data): data is Infer<S>

类型守卫,收窄 data

import { string, number, parse, safeParse, check } from 'litetype'

const User = { name: string.min(1), age: number.min(0) }

parse(User, input)                    // typed data | throw
const r = safeParse(User, input)      // r.success ? r.data : r.error
if (check(User, input)) input.name    // input 收窄为 { name; age }

失败收集整棵树所有错误(不在首错中断),每条 Issuepath + message + code


类型

import { type Infer, type InferInput, type Schema, type Shape } from 'litetype'

const User = { name: string, "email?": string.email() }
type User = Infer<typeof User>        // { name: string; email?: string }  ← Output
type UserIn = InferInput<typeof User> // transform/default 的 Input 侧
  • Infer<S> — 取 Output(校验后的值类型)。

  • InferInput<S> — 取 Input(transform 入参、default/optional 字段 Input 侧可选)。

  • Schema<T> / Shape — 叶/复合节点与裸 shape 的类型。


基础类型(leaf)

import { string, number, boolean, date, unknown } from 'litetype'

导出

Infer

备注

string

string

number

number

接受 InfinityNaN 拒);排除无穷写 .finite()

boolean

boolean

date

Date

Date 实例(非字符串)

unknown

unknown

接受一切,类型保持 unknown

string 的方法

每个都接可选 message 末参,给了覆盖默认错信息。

string.min(n, msg?)          // 长度 >= n
string.max(n, msg?)          // 长度 <= n
string.length(n, msg?)       // 长度 === n
string.email(msg?)           // 词法判定(禁正则)
string.url(msg?)             // new URL() 可解析
string.uuid(msg?)            // 8-4-4-4-12 词法判定
string.datetime(msg?)        // ISO datetime 词法判定
string.ip(msg?)              // IPv4 + IPv6 词法判定
string.startsWith(s, msg?)
string.endsWith(s, msg?)
string.includes(s, msg?)
string.regex(re, msg?)       // 唯一允许的正则:校验用户 data,非解析结构化文本

number 的方法

number.min(n, msg?)          // >= n
number.max(n, msg?)          // <= n
number.gt(n, msg?)           // > n
number.lt(n, msg?)           // < n
number.int(msg?)             // 整数
number.finite(msg?)          // 排除 Infinity / -Infinity
number.positive(msg?)        // > 0
number.nonnegative(msg?)     // >= 0
number.multipleOf(n, msg?)   // 整除 n(整数缩放,不吃浮点误差)

date 的方法

date.min(d, msg?)            // >= d(Date)
date.max(d, msg?)            // <= d

组合子(所有 schema 都有的方法)

string/number 等 leaf 与 array/union/record 等复合节点同构持有这 6 个方法,可任意链式。

import { string, number } from 'litetype'

string.transform(s => s.length)     // 校验后变换值,Output 可异于 Input
number.refine(n => n % 2 === 0, '必须是偶数')  // 加谓词,不改类型
string.default('user')              // 缺值(undefined)时填默认,Input 侧可省
string.optional()                   // 放行 undefined(值层,与 key 的 "?" 正交)
string.nullable()                   // 放行 null
number.nullish()                    // 放行 undefined + null

裸 shape({name: string})无方法,对它整体变换用自由函数 transform(shape, fn)

自由函数版(裸 shape 用)

import { transform, fallback } from 'litetype'

transform({ a: string }, obj => obj.a)        // 对裸 shape 做 transform
fallback(number, 0)                           // 校验失败吞 issue 返回兜底值(zod 的 .catch())

fallback 是自由函数不是方法(catch 是保留字,且给 Schema 接口加 this-返回方法会撑爆类型机)。 default 兜「缺」、fallback 兜「坏」。


复合类型

import { array, union, tuple, record, enum_, literal, lazy } from 'litetype'

array(string.min(1))                       // T[]
union(literal('a'), literal('b'))          // 变长参数,全败 collect-all 报每支原因
tuple(string, number)                      // [string, number],变长参数
record(number)                             // { [k: string]: number }
record(enum_(['a', 'b']), number)          // 两参:key 也校验,key 类型收窄
enum_(['admin', 'user'])                   // 数组参数
literal('user')                            // 字面量
lazy(() => Tree)                           // 递归唯一逃逸舱(JS 字面量无法自引用)

const Tree = { value: string, "children?": array(lazy(() => Tree)) }

discriminatedUnion — 判别联合

import { discriminatedUnion, literal, number } from 'litetype'

const Shape = discriminatedUnion('type',
  { type: literal('circle'), radius: number },
  { type: literal('square'), side: number },
)
// 按判别 key O(1) 选支,只校验那支,错误精准落在那支

每支是裸 shape,判别 key 必须是 literal(...)


对象操作(schema 即数据,原生 JS 全可用)

schema 是裸值,所以不需要 .extend()/.pick()/.omit() —— spread 是 extend,挑字段是 pick,解构 rest 是 omit。可选键("key?")全程保留。

const Post = { title: string, ...Timestamps }              // extend / merge(spread)
const Admin = { ...User, role: literal('admin') }          // 覆写(后者赢)
const Pub = { name: User.name, "email?": User["email?"] }  // pick(手挑)
const { age: _, ...NoAge } = User                          // omit(解构 rest)

partial / required — 批量改 key 可选性

import { partial, required } from 'litetype'

partial(User)    // 每个 key 加 "?"  → Infer = Partial<User>
required(User)   // 每个 key 去 "?"  → Infer = Required<User>

纯 shape 字符串变换,零节点,只作用裸 shape。


strict — 闭集(拒多余 key)

import { strict } from 'litetype'

const Exact = strict({ name: string, "age?": number })
parse(Exact, { name: 'Ann', role: 'x' })   // FAIL: role unexpected key

默认忽略多余 key;strict 只作用本层,类型透明(Infer<strict(S)> === Infer<S>)。


coerce — 掰输入类型再校验

import { coerce, number } from 'litetype'

coerce.number()                  // '42' / 42 → 42(Infer=number, InferInput=unknown)
coerce.boolean()                 // 'true' / 'false' → 布尔(严格白名单)
coerce.date()                    // '2020-01-01' → Date 实例
coerce.string()                  // → string
coerce.number(number.min(0))     // 细化约束写进 inner

表单 / query string / env var 这类「输入永远是字符串」的边界用它。


describe — 挂人读描述

import { describe, descriptionOf, string } from 'litetype'

const Q = describe(string, '搜索关键词')   // 给 schema 挂描述(toJsonSchema / 模型工具用)
descriptionOf(Q)                          // '搜索关键词'

describe 是自由函数 + 冻结透传节点,零行为变化。


错误投影(flatten / treeify / prettify)

error.issues 是扁平 { path, message, code }[]。三个自由函数重整成消费场景形状,吃 SchemaError 或裸 issues[] 都行。

import { flatten, treeify, prettify, safeParse, array, string } from 'litetype'

const r = safeParse({ name: string.min(2), tags: array(string.min(1)) }, { name: 'x', tags: ['ok', ''] })
if (!r.success) {
  flatten(r.error)
  // { formErrors: [], fieldErrors: { name: ['length must be >= 2'], tags: ['length must be >= 1'] } }
  // 单层投影(zod v3 .flatten() 同形)。表单 / API 400 体最常用。

  treeify(r.error)
  // { errors: [], properties: { name: {errors:[...]}, tags: { errors:[], items: [null, {errors:[...]}] } } }
  // 沿 path 下钻的镜像树(zod v4 treeifyError 同形)。深层嵌套精确定位。

  prettify(r.error)   // 也可 prettify(r.error.issues)
  // ✖ length must be >= 2\n  → at name\n✖ length must be >= 1\n  → at tags.1
  // 人读多行串,日志 / CLI。
}

Issue 的形状

import { type Issue, type IssueCode, SchemaError } from 'litetype'

interface Issue {
  path: ReadonlyArray<PropertyKey>   // 错误位置
  message: string                    // dev-facing 英文默认(可逐 check 覆盖)
  code: IssueCode                    // 机读码,消费侧按 code 分支不靠匹配 message
  params?: Record<string, unknown>   // 结构化约束参数(i18n / 反读用)
}

IssueCode 取值:invalid_type invalid_value too_small too_big not_multiple_of invalid_string unrecognized_key invalid_union custom


Standard Schema 生态

实现 Standard Schema ~standard 接口,接入 tRPC / react-hook-form / TanStack。叶子天然兼容;裸 shape 在生态边界包一次 standard()

import { standard } from 'litetype'

t.procedure.input(standard(User))
useForm({ resolver: standardSchemaResolver(standard(User)) })

JSON Schema 互转(litetype/jsonschema)

走子路径,import 'litetype' 零增重。接 ajv 用户群 / 出 OpenAPI / 前端表单。

import { fromJsonSchema, toJsonSchema } from 'litetype/jsonschema'
import { string, number, parse } from 'litetype'

// JSON Schema → schema → 校验真数据
const sch = fromJsonSchema({
  type: 'object',
  properties: { name: { type: 'string', minLength: 1 }, age: { type: 'integer', minimum: 0 } },
  required: ['name', 'age'],
})
parse(sch, { name: 'al', age: 3 })

// schema → JSON Schema(约束随关键字一起反读出来)
toJsonSchema({ id: string.uuid(), age: number.int().min(0) })
// { type:'object', properties:{ id:{type:'string',format:'uuid'}, age:{type:'integer',minimum:0} }, required:[...] }

约束往返string.min(1)/email/uuid/number.int().min(0)/multipleOf 等 JSON-Schema-可表达约束经 toJsonSchema → fromJsonSchema 重建后仍 enforce(不只保 type)。

支持映射:string + minLength/maxLength/format(email|uri|uuid|date-time)/patternnumber/integer + minimum/maximum/exclusiveMinimum/exclusiveMaximum/multipleOfarray/object/enum/const/oneOf|anyOf/$ref(→lazy)/additionalProperties:false(→strict)

边界(文档化,非 bug):

  • 无标准关键字的约束(startsWith/endsWith/includes/ip)不反读 —— 仍只产 type,不谎报。

  • transform/refine/coerce 是运行时变换/谓词,无 JSON Schema 对应 —— toJsonSchema 命中即抛 cannot serializefromJsonSchemaallOf/if-then/patternPropertiesunsupported,不静默吞。