TypeScript DTO 契约怎么保持稳定
· 阅读需 2 分钟
在 Node.js 项目里推广 TypeScript 时,我最建议优先收口的一层就是 DTO。它直接影响接口输入输出,也最容易在多人协作里变成“大家都以为自己理解一致,实际上并没有”的地方。
真正难的不是把类型写出来,而是让它在项目演化里保持稳定。
DTO 不要直接复用数据库模型
把 Sequelize 或 Mongoose 模型类型直接当接口返回值,看上去省了一层定义,实际上会让存储层细节泄漏到接口层。比如内部状态字段、删除标记、时间戳命名方式,往往都不是前端真正想依赖的契约。
DTO 更适合作为独立边界存在。
输入 DTO 和输出 DTO 也别混成一套
创建接口、更新接口、详情接口、列表接口,字段语义通常并不一样。把它们全部塞进一个大类型里,再靠大量可选字段去兼容,最后只会越来越难懂。
按场景拆开,虽然文件会多一点,但阅读成本明显更低。
tsconfig 的模块配置要和运行环境说同一种语言
2021 年很多团队第一次在 Node 服务里正式用 TS,最常遇到的坑不是业务逻辑,而是模块制式不统一。module、moduleResolution、esModuleInterop 这些配置一旦和实际执行方式不一致,就会出现“编译没事,运行报错”的情况。
所以比起到处补兼容写法,更重要的是先定清楚:这个服务到底按 CommonJS 还是按 ESM 跑。
DTO 文件组织要靠近边界,而不是靠近工具
我更喜欢把 DTO 放在接口模块或 service 模块旁边,而不是统一扔到一个巨大 types 目录里。这样当你看某个业务模块时,能很快知道它暴露什么输入输出,不需要在全局类型海里找半天。
小结
DTO 的价值不只是“让 TS 更完整”,而是稳定接口契约。只要坚持边界独立、场景拆分,并让 tsconfig 和运行方式保持一致,Node.js 服务里的 TypeScript 会顺手很多。
