JSON 工具箱

Schema 怎么生成 TypeScript 类型

结论:用 json-schema-to-typescript 或 quicktype 一条命令就能产出 .d.ts,但生成结果只是起点:可选字段、联合类型、`additionalProperties` 的映射都需要人工确认,否则类型看着对、用起来处处报错。

进阶约 4 分钟发布于 2026-10-30更新于 2026-10-30
从 JSON Schema 生成 TypeScript 类型的流程与坑

结论:用 json-schema-to-typescript 或 quicktype 生成只算第一步,随后必须人工确认可选字段、联合类型、additionalProperties 与日期格式;Schema 是源、类型是产物,并在 CI 加 types:check。

一条命令就能出结果,但生成的 .d.ts 只是草稿,不是成品。

最快的一条命令

npx json-schema-to-typescript schema.json > src/types/order.d.ts

它支持 $ref、allOf、oneOf、anyOf,会把 description 转成 JSDoc 注释,输出带 export interface 的声明文件。 需要一次生成多个文件、加 banner、控制 style(interface 或 type)时,写个 build-types.mjs 调它的 API 更合适。

另一个常用工具是 quicktype:

npx quicktype -s schema schema.json -o order.ts --lang typescript

它的强项是从「样例 JSON」直接推类型,也能吃 Schema,适合多语言项目统一生成。

生成之后必须确认的四件事

一、可选字段的边界。 Schema 里没写进 required 的字段,生成器一律标 ?。但业务上「字段必须存在、值可以为 null」和「字段可以不存在」是两回事:

interface Order {
  remark?: string | null;   // 可能不存在,也可能是 null
  status: string;           // 必填
}

如果后端实际总是返回 remark: null,把 ? 去掉,前端就不用到处判 undefined。

二、oneOf 生成的联合类型。 生成器通常直接输出 A | B,但如果两个分支没有可辨识字段(discriminator),TypeScript 无法收窄,用的时候还得写类型守卫。这种情况下建议在 Schema 里加一个固定的 type 字段,让联合类型可辨识。

三、additionalProperties。 写成 true 或省略时,有的生成器会产出 [k: string]: unknown,有的是 [k: string]: any。前者安全,后者会让整个对象失去类型检查。看到 any 就该改。

四、格式字段。 format: "date-time" 在 JSON 里是字符串,生成器默认给 string。要改成 Date 就得自己接管反序列化——这一步不做,运行时拿到的还是字符串,类型写着 Date 反而制造 bug。多数项目的正确选择是保留 string,在边界处显式转换。

方向要定死

Schema 与类型的关系必须是单向的:Schema 是源,类型是产物。

反过来做(先写 TS 类型,再用工具生成 Schema)会导致两边都能改,最终没人知道哪边是对的。如果确实需要从类型生成 Schema,也要约定「类型改动后必须立即重新生成并提交 Schema」,且在 CI 里加一致性检查。

放进流水线

# package.json
"scripts": {
  "types:gen": "json2ts -i schemas/ -o src/types/generated --bannerComment \"/* eslint-disable */\"",
  "types:check": "pnpm types:gen && git diff --exit-code src/types/generated"
}

types:check 是关键:Schema 改了但没重新生成,CI 直接失败。没有这一步,生成的类型迟早和 Schema 脱节。

常见问题

生成出来的类型为什么会出现 any?

多来自 additionalProperties 写成 true 或被省略,有的生成器会产出 [k: string]: any。看到 any 就应改成 unknown 或补全具体字段。

应该如何确认可选字段的边界?

Schema 里没写进 required 的字段一律被标 ?,但「字段必须存在、值可以为 null」和「字段可以不存在」是两回事。后端实际总是返回该字段时,把 ? 去掉。

是否可以让 CI 检查类型与 Schema 是否同步?

可以。加一条 types:check:重新生成后执行 git diff --exit-code,Schema 改了却没重新生成时 CI 直接失败。

相关工具

延伸阅读