Skip to content

TypeScript 配置

概述

Walnut Admin 是一个异构工具链的全栈 monorepo——前端用 ESM + Vite + moduleResolution: "bundler",后端用 CJS + NestJS CLI + SWC。TypeScript 的配置策略需要同时兼容这两种截然不同的模块系统。

我们做了什么

1. 根 tsconfig.base.json 只放纯语言级选项

tsconfig.base.json 是所有前端 workspace 包的共享基线:

jsonc
{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "bundler",     // Vite/esbuild 原生理解
    "strict": true,
    "isolatedModules": true,           // Vite/SWC 的前提条件
    "verbatimModuleSyntax": true,      // 强制区分 type import
    "skipLibCheck": true,              // 跳过 node_modules 类型检查
    "noEmit": true                     // 类型仅检查,不产出文件(Vite 负责构建)
  }
}

2. 前端包 extends root base

apps/adminpackages/platform-web/clientpackages/platform-web/http 等前端包直接 extends root base,仅声明自己的 include/paths

jsonc
// apps/admin/tsconfig.json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "paths": { "@/*": ["./src/*"], "~/*": ["./types/*"] }
  }
}

2.1 DOM lib 下沉(2026-08-08)

tsconfig.base.json 只保留 lib: ["ESNext"]——DOM/DOM.Iterable 下沉到真正运行在浏览器的包(apps/adminapps/docspackages/platform-web/*)各自声明。platform-any 的 contract/types/utils-core 不再可见 window/document 等 DOM 全局,杜绝"平台无关包写出浏览器专用代码"的假阴性。typeRoots 同样从 base 移除,恢复 TypeScript 默认的逐级 node_modules/@types 自动发现。

3. 后端不 extends root base

apps/server/tsconfig.json完全独立的——不 extends tsconfig.base.json。原因:后端使用 CJS + moduleResolution: "node" + experimentalDecorators(NestJS 必需),与 root base 的 ESM + bundler 完全冲突:

选项root base(前端)server(后端)
moduleESNextcommonjs
moduleResolutionbundlernode
experimentalDecoratorstrue
emitDecoratorMetadatatrue
noEmittruefalse(SWC 需要 .js)
verbatimModuleSyntaxtrue与 CJS 不兼容

2026-08-08 收紧:后端启用 strict: true(仅保留 strictPropertyInitialization: false——NestJS 依赖注入属性由构造器装饰器初始化)。全仓 tsc --noEmit 零错误通过,含 noImplicitAny/strictBindCallApply/strictFunctionTypes

4. 不用 TypeScript Project References

ADR-0010 明确否决了 Project References。

原因:

  • 异构工具链不兼容(bundler vs node 模块解析)
  • Vite/VitePress 不读 references 字段
  • 后端有自己的 tsconfig,不参与交叉引用
  • Turbo 的 dependsOn: ["^build"] 已解决构建顺序问题,不需要 TS 层面的引用

5. 类型检查作为独立任务

任务前端后端
类型检查vue-tsc --noEmittsc --noEmit
构建vite build(不做类型检查)nest build(SWC 编译)

turbo.jsontypes:checkbuild两个独立任务,互不影响。

没做什么 / 为什么

不提取共享 tsconfig 包(@repo/tsconfig

当前有 6 个共享包(contract / types / utils-core / client / http / ui)+ 3 个 tooling 包(eslint-config / release / commitlint-config)+ 3 个 app 需要 tsconfig,root base + 各自 extends 已足够。提取成 @walnut/tsconfig 包的主要收益是版本控制和外部消费,当前规模下不需要。

不声明跨包 paths

按照 ADR-0012@walnut/contract@walnut/utils 在后端中通过 pnpm workspace symlink + package.jsonexports 字段解析,不通过 tsconfig paths。这确保了开发环境和生产环境(如发布到 npm)的解析行为一致。

不直接参考后端的 tsconfig

后端的 @walnut-server/* 内部 lib 通过 tsconfig paths 映射(而非 pnpm workspace),这是它们唯一可行的解析方式——这些 lib 不是 pnpm workspace 包,没有 package.jsonexports 字段。


相关 ADR

基于 MIT 许可发布