前言
在 TypeScript 项目中,我们能够像使用其他静态类型语言那样定义数据类型,以此来规范数据结构。值得注意的是,TypeScript 的类型检查仅在编译阶段执行;到了运行时,这些类型信息会被擦除,因此无法直接访问或利用它们来进行类型检查。
为了实现在运行时的数据有效性验证,通常需要引入额外的方案,例如 JSON Schema。然而,当项目中的类型较多时,手动编写 JSON Schema 可能会消耗大量时间,并且每次 TypeScript 类型发生变更时,都需要相应地调整 JSON Schema 规则,这无疑增加了维护成本。
因此,我们需要一个自动化的工具,可以根据 TypeScript 类型自动生成相应的 JSON Schema。本文采用了ts-json-schema-generator来生成对应的 JSON Schema 文件。
项目环境
项目基于Vite+TypeScript构建,因此相关配置也是针对vite项目。
安装ts-json-schema-generator
yarn add ts-json-schema-generator -D
配置Vite插件
自定义vite插件,并注册到vite中,使相关ts类文件修改时可以自动生成JSON Schema。
- 首先在项目下创建plugins文件夹,在里面新增jsonchema-generator.js文件,内容如下:
// plugins/jsonchema-generator.js
const path = require('path');
const fs = require('fs');
const tsj = require("ts-json-schema-generator");
const watchFile = path.resolve(__dirname, '需要自动生成JSON Schema的TS类文件路径')
export default () => {
return {
name: 'jsonschema-generator',
configureServer(server) {
server.watcher.on('change', (file) => {
if (watchFile === file) {
runMyScript();
}
});
},
};
function runMyScript() {
const config = {
path: watchFile,
tsconfig: path.resolve(__dirname, '../tsconfig.json'),
type: "*",
};
const outputPath = path.resolve(__dirname, '生成的JSON Schema文件路径');
const schema = tsj.createGenerator(config).createSchema(config.type);
const schemaString = `export default ${JSON.stringify(schema, null, 2)}`
fs.writeFile(outputPath, schemaString, (err) => {
if (err) {
console.error("生成Schema失败:", err)
} else {
console.log('重新生成Schema!')
}
});
}
}
- 在vite.config.ts文件中注册插件
import jsonschemaGenerator from './plugins/jsonschema-generator.js' // 引入
export default defineConfig({
...
plugins: [..., jsonschemaGenerator()], // 在plugins中注册
})
利用ajv校验
完成上述步骤后,就已经配置完毕了,接下来可以修改或者新增对应文件中的类来观察Schema文件是否已经自动生成。
接下来,就利用ajv库来使用生成的JSON Schema。
- 首先安装ajv库
yarn add ajv
- 使用ajv校验数据
import Schema from "生成的Schema文件";
// 待校验的数据
const data = {
...
}
const ajvIns = new Ajv({allErrors: true})
ajvIns.compile(Schema)
const schemaValidate = ajvIns.getSchema('XXX') // 读取某个类的Schema
if (schemaValidate) {
console.log(schemaValidate(data), schemaValidate.errors)
}