Skip to content
返回博客
教程

JSON 转 TypeScript:生成接口与类型完全指南(2026)

正确地把 JSON 转成 TypeScript 接口:类型推断规则、interface 与 type 之争、可选与联合字段处理,以及要避开的工程陷阱。免费在线试用。

11 分钟阅读

JSON 转 TypeScript:生成接口与类型完全指南(2026)

从 JSON 到 TypeScript 最快的路径,就是把你的数据粘贴进 JSON 转 TypeScript 在线工具,复制它生成的 interface。免安装、不上传,整个过程在浏览器里完成。如果你现在就要一份类型,这就够用了。

但能生成类型和能生成好类型是两码事。转换器必须靠猜:这个字段是可选的,还是只是某一份样本里碰巧没出现?那个数组永远是字符串,还是有时也混着数字?日期字符串该不该变成 Date?本文会讲清推断到底是怎么工作的、什么时候该用 interface 而不是 type,以及把线上 API 响应变成可信类型时的真实陷阱。

如何把 JSON 转成 TypeScript

把 JSON 转成 TypeScript 只需三步:

  1. 粘贴 JSON。 把一个对象、数组或原始 API 响应丢进输入框。转换会瞬时完成,完全在客户端运行。
  2. 调整输出。interfacetype,设置一个根名称(比如 UserApiResponse),切换 export 关键字,并为可选字段选择 ?: 还是 | null
  3. 复制或下载。 一键拿到生成的 TypeScript,直接粘进你的代码库。

操作层面就这么简单。JSON 转 TypeScript 在线工具 读取输入、推断出一棵类型树,并在右侧打印定义,任何数据都不会离开页面。本文余下部分要做的,是帮你理解它生成了什么,好让你修正那些它没法猜对的情况。

JSON 类型如何映射到 TypeScript

每个 JSON 值都有对应的 TypeScript 类型。这个映射基本是一一对应的:

JSON 值TypeScript 类型
"text"string
423.14number
true / falseboolean
nullnull
[1, 2, 3]number[]
{ ... }interfacetype

真正需要费心的是对象。看一个典型的 REST 用户数据:

{
  "id": 101,
  "name": "Ada Lovelace",
  "email": "ada@example.com",
  "active": true,
  "roles": ["admin", "user"]
}

转换器会从这段 JSON 生成下面这个 TypeScript 接口:

export interface User {
  id: number;
  name: string;
  email: string;
  active: boolean;
  roles: string[];
}

每个标量映射到对应的原始类型,roles 数组变成 string[]。把它丢进你的 API 客户端,就能免费获得自动补全和编译期检查。

转换器如何推断类型

真正讲究的是推断算法。四条规则几乎能覆盖你扔给它的所有输入。

结构推断:每个对象生成一个命名 interface

每种不同的对象结构都会成为各自的命名 interface。嵌套对象不会被压成内联类型,而是被提升为独立的、可被引用的定义:

{
  "order": {
    "id": "A-1",
    "total": 42.5,
    "customer": { "name": "Sam", "vip": false }
  }
}
export interface Root {
  order: Order;
}

export interface Order {
  id: string;
  total: number;
  customer: Customer;
}

export interface Customer {
  name: string;
  vip: boolean;
}

相同的结构会被去重,因此两个结构一致的字段会共用同一个 interface,而不是生成副本。

数组合并:推断可选字段

当你传入一个对象数组时,转换器会逐键合并这些对象。如果某个键只出现在部分元素里、而不是全部,它就会被标记为可选:

{
  "users": [
    { "id": 1, "nick": "x" },
    { "id": 2 }
  ]
}
export interface Root {
  users: User[];
}

export interface User {
  id: number;
  nick?: string;
}

id 出现在每个元素里,所以保持必填。nick 在第二个用户里缺失,于是变成 nick?: string。这就是为什么单份样本往往不够用,下文的陷阱小节会展开讲。

混合数组的联合类型(union)

数组不一定是同质的。当元素类型各异时,转换器会把它们收集成一个联合类型:

{
  "tags": ["a", "b"],
  "meta": [1, "two"]
}
export interface Root {
  tags: string[];
  meta: (string | number)[];
}

tags 全是字符串,所以是 string[]meta 混了一个数字和一个字符串,于是生成 (string | number)[]。空数组则完全无法推断,会回退为 unknown[],这其实很诚实,因为零个元素本就提供不了任何信息。

可选 vs null:?:| null 之别

这两者看起来相似,含义却不同。标记为 ?: 的字段可能整个从对象里缺席;而标注 | null 的字段总是存在,只是可能持有 null 这个值:

{ "score": null }
export interface Root {
  score: number | null;
}

这里 score 是存在的,只是显式为 null,所以类型是 number | null,值确实存在,只不过是 null。对比前面数组示例里的 nick?: string,那种情况下整个键都可能不在。当 API 可能省略某个键时用 ?:,当它会带着 null 值发来这个键时用 | null。大多数转换器都用一个开关让你控制这块的渲染方式。

interface vs type:到底该用哪个?

两者都能描述一个对象结构。下面是取舍方法:

需求
来自 JSON 的普通对象结构interface
联合(A | B)或交叉(A & Ctype
跨文件的声明合并 / 扩展interface
映射类型或条件类型type
编辑器错误信息略微更干净interface

对于从 JSON 对象里出来的数据,interface 是约定俗成的选择,且能产生略好一点的编译器错误信息。一旦你需要联合类型,比如一个要么成功、要么报错的 Result,就必须切到 type,因为 interface 无法表达联合:

type ApiResult =
  | { status: "ok"; data: User }
  | { status: "error"; message: string };

一个不错的默认策略:为普通 JSON 对象生成 interface,当结构需要联合或交叉时再转成 type。如果想看完整对比,TypeScript Handbook 覆盖了各种边界情况。当你需要的是 json to typescript type 别名而非 interface 时,大多数转换器都提供一个开关,一键翻转整个输出。

quicktype vs json-to-ts vs 在线工具 vs 手写

生成 TypeScript 类型没有唯一的最佳方式,要看 JSON 在哪、以及它多久变一次。

方式适合取舍
quicktype多语言输出、生成运行时校验代码偏重;输出常多于你的实际所需
json-to-ts(库)接入脚本或流水线的构建期代码生成需要配置和 Node 工具链
浏览器在线工具一次性转换、敏感数据、零安装每次都要手动粘贴
手写类型小对象、学习类型系统规模一大就繁琐且易错

需要多种语言的类型、或想让它在生成类型的同时一并产出校验器时,选 quicktype。当转换该归属于一个构建步骤时,选 json-to-ts 库。而对于快速转换,尤其是含有令牌、客户 ID 或任何你不愿粘进远程服务的数据时,浏览器工具在隐私和速度上更占优。我们的 JSON 转 TypeScript 在线工具 完全在客户端运行:JSON 永不离开页面,也没有任何东西需要安装或更新。

类型不是校验:衔接到运行时检查

这一点把很多团队绊倒过,所以要说清楚:TypeScript 类型在编译期就被擦除,运行时什么也不做。一个生成出来的 interface User 不会去检查真实的 API 响应是否与之相符。如果服务器把 id 发成了字符串而非数字,TypeScript 会乖乖相信你的类型标注,而真实数据却在撒谎。

要既 generate typescript types,又能在线上数据上强制执行它们,就得把类型和运行时校验器配对使用:

  • zod——定义一次 schema,从中推断出静态类型,并在运行时解析响应。
  • io-ts——基于编解码器(codec)的校验,在函数式代码库里很受欢迎。
  • JSON Schema + Ajv——语言无关的 schema,在运行时校验,适用于契约需要跨服务共享的场景。

一种常见做法是:生成 interface 供编辑器支持用,再写一个对应的 zod schema 作为真正的边界检查:

import { z } from "zod";

const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
  active: z.boolean(),
  roles: z.array(z.string()),
});

type User = z.infer<typeof UserSchema>;

// 如果真实响应与 schema 不符,则抛出异常。
const user = UserSchema.parse(await res.json());

如果你打算走 schema 这条路,我们的 JSON Schema 验证完全指南 会从头到尾讲清如何编写并强制执行 schema。

给 JSON 标类型时的常见陷阱

生成出来的类型是起点,不是成品。下面这几处要留意。

键名大小写风格会原样保留。一个 GitHub 风格、带 public_repos 的数据会生成 public_repos: number,而不是 publicRepos。如果你的代码库偏好 camelCase,那就要么接受这些 snake_case 类型,要么加一个映射层,在数据进来时重命名字段。

日期字符串仍然是 string。像 "2026-06-01T00:00:00Z" 这样的字段会被标为 string,而不是 Date。这是刻意为之,JSON 没有日期类型,而猜错比诚实更糟。从字符串到 Date 的转换属于你的解析层,那里由你掌控。

anyunknown 会泄漏出来。空数组会变成 unknown[],深度混杂的结构则可能退化为松散类型。这些都不是 bug,而是转换器在承认数据不足、无从推断。等你知道了真实结构,再手动收紧它们。

深层嵌套会爆炸。一份高度嵌套的数据会生成一长串小 interface。这通常没问题,而且比一个巨大的内联类型更易读,但那些不值得拥有独立名字的结构,仍值得拍平。

最关键的是单样本陷阱。可选字段只能从多个数组元素里推断出来,单个对象无法告诉转换器哪些键有时会缺席。先收集一组有代表性的响应数组。在转换前,JSON 格式化工具 很适合把多份样本粘进去、并排查看;而如果你喂的是 JSONC 这类非标准输入,请读我们的 从 JSON5 到 JSONC,转换器需要的是严格合法的 JSON。

常见问题

我该怎么把 JSON 转成 TypeScript 接口?

把 JSON 粘贴进 JSON 转 TypeScript 在线工具。它会在你的浏览器里读取输入,并即时生成一个 TypeScript 接口。点击「复制」即可拿到结果,无需上传、无需账号、不用装插件。

JSON 数据该用 interface 还是 type

JSON 来的普通对象结构用 interface,这是惯例,且能给出略微更清晰的编辑器错误。当你需要联合或交叉类型时切到 type,因为 interface 无法表达这些。

嵌套对象和数组是怎么处理的?

嵌套对象会变成独立的命名 interface(一个 address 字段会得到一个 Address interface)。对象数组会被逐键合并成一个元素 interface;原始类型数组则变成 string[] 这样的类型化数组。

可选字段和 null 字段是怎么标类型的?

某些数组元素里缺失的键会被标为可选(nick?: string)。一个总是存在但持有 null 的键则用 | null 标注(score: number | null)。两者描述的是不同情形:缺席,与存在但为 null。

TypeScript 类型会在运行时校验 JSON 吗?

不会。TypeScript 类型在编译期被擦除,运行时不会检查数据。要校验真实的 API 响应,请把生成的类型与运行时校验器配对,比如 zod、io-ts,或 JSON Schema 配 Ajv。

quicktype、json-to-ts 还是在线转换器,哪个最好?

quicktype 适合多语言输出和运行时校验器;json-to-ts 库适合构建期代码生成;在线转换器适合快速、私密、一次性的转换且零安装。对于敏感数据,客户端浏览器工具能让数据不进入任何服务器。

为什么日期字符串被标为 string 而不是 Date

JSON 没有日期类型,所以传输线上的日期不过就是个字符串。把它标为 string 既诚实又稳定;转成 Date 是你解析层的决定,那里由你掌控格式和错误处理。

用在线转换器时我的 JSON 数据私密吗?

用客户端工具的话,是的。转换完全在你的浏览器里用 JavaScript 运行,所以 JSON(包括令牌、ID 和客户数据)永不离开页面,也绝不会被发送到服务器。