一个可以根据 mongoose schema 生成 model 类型的工具
MongoDB 在 NodeJS 社区有着非常广泛的使用. 虽然灵活是 MongoDB 的一大优势, 但是真正业务中不希望它 ‘过于灵活’, 因此一般都会搭配 mongoose 来使用, 所以基本也会定义 Schema
.
由于 mongoose 出现很早, 它是以 Object 的形式来定义 Schema. 但是随着 Typescript 的流行, ts 和 mongoose 结合使用时, 定义 Model 类型时不能直接使用 Schema 类型, 所以我们一般都要根据对应的 Schema 类型再写一个类型: type UserModel = mongoose.Model<UserType>
.
综上, 我决定写一个自动化工具来解放双手, 减少人工失误.
工具使用介绍⌗
原理⌗
因为 mongoose Schema 已经是结构化的了, 所以这件事情基本分为两步, 将 Schema 转化成方便处理的类型和生成最终代码.
parser⌗
首先想想我们需要的字段信息:
- type
- required
- enum
- isArray
- ref
对于基本类型(‘ObjectID’, ‘String’, ‘Number’, ‘Date’, ‘Boolean’, ‘Mixed’, ‘Buffer’, ‘Map’, ‘Decimal128’), 上面的这些包含了我们想要的信息.
接着要处理数组类型, 嵌套类型, 嵌套 Schema 这几种情况.
对于数组类型⌗
可以看出 instance 类型是 Array
, caster
字段就是数组元素的类型信息, 递归处理.
对于嵌套 Schema⌗
可以看出 instance 类型是 Embedded
, schema
字段是子文档的类型信息, 递归处理.
(直接)嵌套类型⌗
此种类型 Schema 处理的比较诡异, 结构被扁平化处理了, 所以我们需要先把它结构恢复回去, 并标记成 Embedded
类型.
于是我们就可以把每一个字段处理成下面这个结构:
注 相关处理代码可在 zcong1993/mongoose-schema-parser 查看.
生成代码⌗
有了结构化的数据, 把他们转化成 TS 代码并不难. 但是如果直接使用字符串拼接, 不太具有结构化, 而且缩进处理起来不是很舒服, 代码看起来也不清晰. 所以我们选择 ts-morph 这个库来生成代码.
简单介绍下它的使用:
可以看到代码非常清晰, 而且缩进之类的也会处理得当.
言归正传, 需要我们做的其实是两点:
- 基本类型处理成 ts 类型或者 mongoose 提供的类型
- 复杂类型递归生成子类型结构
基本类型对应 ts 类型可参考下面表格:
MongoDB | TS |
---|---|
Boolean | boolean |
String | string |
Number | number |
Date | Date |
ObjectID | mongoose.Types.ObjectID |
Mixed | any |
Buffer | Buffer |
Map | any |
Decimal128 | mongoose.Types.Decimal128 |
注 对于 enum 类型, 可优化为 enum 字面量的联合类型, 例如: type: ‘A’ | ‘B’
遇到 Schema
和 Embedded
类型时, 递归处理生成子类型就好了.
注 相关处理代码可在 ts-mongoose-code-generator 查看.
后记⌗
我一直认为如果更改一个字段声名需要改动两个地方以上, 就很容易出现不一致的情况, 因为人在做重复劳动时总是会犯错, 所以我们需要一些工具来做一些有固定模式化的事情.
但是我觉得 ORM 的未来肯定是基于 注解式
声名, 这样就能统一 Schema 结构和 Model 类型, 就像 typeorm 那样.