如何从指定类型为字符串的静态JSON schema动态生成TypeScript类型
从JSON Schema批量生成F1040 TypeScript类型方案
错误原因说明
你之前的两种方案失效的核心问题一致:导入的JSON文件在TypeScript中默认会被推导为宽泛的Array<{name: string, type: string}>类型,丢失了字段名、字段类型的字面量信息,同时你手动加的as string[]断言进一步抹除了剩余的窄类型信息,最终生成的F1040类型是无字段校验能力的宽泛类型,自然不会触发类型错误。
你第一次写的映射类型语法错误,是因为TS不允许在同一个对象类型声明中写多个独立的in映射子句,需要用交叉类型拼接:
// 语法修复后的方案,前提是bools/strings有字面量类型 export type F1040 = & { [key in typeof bools[number]]?: boolean } & { [key in typeof strings[number]]?: string }
可用方案
方案1:纯TS类型层实现(无额外依赖)
第一步先把f1040.json改为TS文件并加as const断言,保留字面量信息:
// f1040.ts export default [ { name: "L1", type: "String" }, { name: "C2", type: "Boolean" }, { name: "L3", type: "String" }, // 其余字段 ] as const
第二步写映射类型自动生成最终类型:
import f1040 from "./f1040"; type SchemaItem = typeof f1040[number] // 映射JSON里的type到TS原生类型 type MapToTsType<T extends string> = T extends "String" ? string : T extends "Boolean" ? boolean : never // 自动生成带字段校验的类型 export type F1040 = { [Item in SchemaItem as Item['name']]: MapToTsType<Item['type']> }
此时错误赋值会正常触发类型报错:
// 报错:类型“boolean”不能分配给类型“string”(L1字段) const f1040Data: F1040 = { L1: true, C2: "Yea", }
方案2:修复TypeBox实现
只需要给导入的schema加as const保留字面量类型即可:
import { Static, Type } from "@sinclair/typebox"; // 这里导入的f1040已经是加了as const的字面量类型 import f1040 from "./f1040"; const F1040Schema = Type.Object( Object.fromEntries( f1040.map(item => [ item.name, item.type === "Boolean" ? Type.Boolean() : Type.String() ]) ) ); export type F1040 = Static<typeof F1040Schema>; // 此时错误赋值会正常触发校验 const f1040Data: F1040 = { L1: true, C2: "Yea", }
方案3:构建时预生成d.ts(兼容现有rollup流程)
如果不想修改现有JSON结构,可以用Node脚本在构建前预生成静态声明文件,完全兼容你在用的声明打包器:
- 写生成脚本
gen-f1040-type.js:
const fs = require('fs') const f1040 = require('./f1040.json') const typeFields = f1040.map(item => { const tsType = item.type === 'Boolean' ? 'boolean' : 'string' return ` ${item.name}: ${tsType};` }).join('\n') const dtsContent = `export type F1040 = {\n${typeFields}\n}` fs.writeFileSync('./f1040.d.ts', dtsContent)
- 修改
package.json的构建命令,在rollup打包前执行脚本:
{ "scripts": { "prebuild": "node ./gen-f1040-type.js", "build": "rollup -c" } }
生成的f1040.d.ts是静态类型文件,无任何运行时代码,和手动写的声明文件效果完全一致。
内容的提问来源于stack exchange,提问作者bohendo
相关产品推荐
相关产品推荐

