VanaAPI

← 文章

从 JSON 示例生成 TypeScript 类型:分步实战指南

日常活儿ZH2026-10-25

拿到接口返回的 JSON 后,先生成类型,再开始写业务代码,是一个实用的起点。但示例只展示已经出现的数据,不能自动证明完整的接口契约。本篇以用户资料响应为例,完成从样本整理、模型生成到验证入库的流程,把确定的结构和仍需确认的假设分开处理。

§ 第一步:准备有代表性的样本

不要只复制一条字段齐全的成功响应。建议分别收集普通用户、资料未填写用户,以及包含空数组的响应。提交前移除令牌、邮箱、真实姓名等敏感信息,用合成数据替换,同时保留原来的字段结构和值类型。

第一条示例:{"id":"u_101","name":"示例甲","nickname":"小甲","tags":["编辑"],"last_login_at":"2026-09-01T08:00:00Z"}。

第二条示例:{"id":"u_102","name":"示例乙","tags":[],"last_login_at":null}。

两条样本足以展示本次练习的差异,但不要据此认定所有响应都只有这些变化。把接口文档中的字段说明一并准备好;没有文档时,记录需要向接口维护者确认的问题。

§ 第二步:把生成规则写进提示词

与其只说“帮我生成类型”,不如明确输入、输出和不允许猜测的内容。可以使用下面这段提示词,然后附上两条示例:

请根据以下 JSON 样本生成导出的 TypeScript 类型,根类型命名为 UserProfile。保留原始字段名,不使用 any,不添加样本之外的字段。跨样本缺失的属性暂标为可选,并列入待确认项;显式出现 null 的属性保留 null。日期字符串暂用 string。普通示例字符串不要推断成枚举。分别返回类型声明和推断依据。

这里有两个不同概念:nickname?: string 允许属性缺失;last_login_at: string | null 则表示属性存在,但值可以为空。审查时不要把可选属性和可空值混为一谈。([typescriptlang.org](https://www.typescriptlang.org/docs/handbook/2/objects.html?utm_source=openai))

§ 第三步:通过现有工具发起请求

如果已经使用 OpenAI SDK,可以把基础地址配置为 Vana API 的兼容入口,在服务端环境变量中保存密钥,再发送提示词和样本。Vana API 提供 OpenAI 兼容接入,也可配合 Codex 等现有工具使用,因此这项任务不必单独建设一套调用流程。([vanaapi.com](https://vanaapi.com/))

尚未接入的读者可以创建一个 Vana API 账户,用脱敏后的少量样本完成第一次生成。建议先要求返回便于人工审查的文本,不要直接让模型覆盖项目文件。保留输入样本、提示词和输出,方便后续追溯每项类型判断的来源。

§ 第四步:逐项审查生成结果

按照本次提示词,预期得到的声明是:export interface UserProfile { id: string; name: string; nickname?: string; tags: string[]; last_login_at: string | null; }。其中可选属性、数组和联合类型分别表达不同的结构约束。([typescriptlang.org](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html?trk=article-ssr-frontend-pulse_little-text-block&utm_source=openai))

  • 标识符:核对接口约定,确认 id 应保留为字符串,而不是因为某次样本只有数字字符就改成 number。
  • 空数组:结合非空样本确认 tags 的元素类型;如果只有空数组,先补样本,不要猜测内部结构。
  • 日期:确认 last_login_at 表示传输层原始字符串,还是已经经过业务代码转换的值。
  • 可选字段:确认 nickname 的缺失是合法状态,而不是权限、接口版本或采集错误造成的差异。

如果之后出现 status 字段,不要仅凭一次观察就把所有可能值固定下来。只有在接口契约明确列出状态集合时,才据此建立字面量联合;联合类型用于表达允许的选择,而不是替样本补全未知事实。([typescriptlang.org](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html?trk=article-ssr-frontend-pulse_little-text-block&utm_source=openai))

§ 第五步:用编译器检查样本

把声明保存到项目的类型文件,把脱敏样本保存到测试夹具文件。在夹具中写入 const samples: UserProfile[] = [...],然后运行项目已有的类型检查命令,例如 npx tsc --noEmit。建议在项目配置中启用 strict,让检查更严格,而不是通过类型断言绕过报错。([typescriptlang.org](https://www.typescriptlang.org/tsconfig/strict.html))

再设计两个反例:把 tags 中的元素改成数字,以及删除 last_login_at。按照当前声明,它们都应无法通过检查。反例用于验证约束是否生效,不应作为有效夹具留在正常构建路径中。若检查结果不符合预期,先修正声明或测试配置,再继续使用。

§ 第六步:补上运行时校验并持续维护

编译通过不等于外部响应已经接受验证。TypeScript 类型注解在编译后会被移除,因此把响应断言成 UserProfile,不能阻止服务端返回错误结构。需要在数据入口执行实际校验,通过后再交给业务逻辑。([typescriptlang.org](https://www.typescriptlang.org/docs/handbook/2/basic-types.html))

最后,把类型、样本和待确认项一起提交审查。接口新增字段或改变空值规则时,补充对应样本,要求模型解释差异,再重新运行检查。这样,模型负责整理重复工作,接口契约决定边界,测试负责确认修改没有破坏已有约束。