Schema 转 TypeScript 把一份 JSON Schema 转成等价的 TypeScript 类型定义,让前端代码从编辑期就能享受字段补全、类型检查、重构安全。所有转换在浏览器本地完成,不联网。
核心映射:
| JSON Schema | TypeScript |
|---|---|
type: "string" | string |
type: "integer" / "number" | number |
type: "boolean" | boolean |
type: "null" | null |
type: "array", items: T | T[] |
prefixItems: [A, B](2020-12) | [A, B] |
type: "object", properties + required | interface |
enum: [...] | 字面量联合 "a" | "b" |
const: X | 字面量 X |
anyOf / oneOf | 联合 A | B |
allOf | 交叉 A & B |
$ref → #/$defs/X | 引用类型 X |
additionalProperties: T | [k: string]: T 索引签名 |
与主流库的对齐点:字段按 required 列表决定必选 / 可选;对象转 interface、其它转 type;definitions / $defs 提升为顶层类型,命名沿用 Schema 里的 key。description 字段会生成对应 TSDoc 注释(/** ... */)。
工具局限:
integer 和 number 都映射为 TS 的 number(语言限制)pattern / minLength / maximum 等运行时约束无法在类型系统表达,需要配合运行时校验器(如 Schema 校验器)$ref URL(如跨文件、跨域)if / then / else、dependentSchemas)转为 unknown默认对象类型出 `interface`(`export interface User { id: number; name: string }`),其它(enum / union / primitive / tuple)出 `type`(比如 `export type Role = 'admin' | 'user'`)。`$defs` 和 `definitions` 里声明的子 Schema 会被提到顶层作为独立类型,根 Schema 用 `根类型名`(默认 `Root`,可改成 `User` 等)。
anyOf 和 oneOf 都转成 联合类型(`A | B`)——TypeScript 结构类型系统没有"严格互斥"的直接表达,工具按联合处理。allOf 转成 交叉类型(`A & B`)。如果需要严格互斥语义,请在生成的 TS 基础上手动加 discriminated union(按 `type` 字段区分)。
保留引用——`$ref: "#/definitions/Address"` 转成 TS 里的 `Address` 类型引用,前提是 `definitions` / `$defs` 里确实有这个名字。工具不会联网解析外部 `$ref`(如 `https://...`),遇到外部引用会转为 `unknown`。同文件内的循环引用(A 引 B、B 引 A)也能正确处理。
TypeScript 原生没有整数类型——`number` 统一表示所有数值,包括整数和浮点数。Schema 里的 `integer` 约束是运行时规则(由校验器保证),编译期通过 TS 类型系统无法表达。想要严格区分可以用 `integer` 分支类型,但会让类型接入成本飙升,工具默认不生成。
不会,生成联合字面量类型(`type Role = "admin" | "user" | "guest"`)。原因:TS `enum` 会在编译后产生运行时对象,侵入性高;联合字面量类型则纯在编译期,轻量且互操作性更好。这也是 `json-schema-to-typescript` 等主流库的默认做法。
标记为可选(字段名后加 `?`)并允许 `undefined`。Schema 里 `required: ["id", "name"]` 时,`email`、`role` 这些没列入 required 的字段在 TS 里变成 `email?: string`。这与 JSON Schema 的默认语义一致:没列入 `required` 就是可以缺省。