【声明】本博客所有内容均为个人业余时间创作,所述技术案例均来自公开开源项目(如Github,Apache基金会),不涉及任何企业机密或未公开技术,如有侵权请联系删除
背景
上篇 blog
【Agent】【OpenCode】项目配置(tsconfig.json 与 package.json)
继续分析了 export 的配置,export 指向的是源码 .ts 文件,而不是 dist/ 构建产物,因为 Bun 原生支持直接执行 TypeScript,所以支持 exports 通配符可以直接指向源码,接着分析了 package.json 和 tsconfig.json,子包下唯二的两个 JSON 配置文件,有子包的地方,除了有 package.json,还有 tsconfig.json,其中 package.json 是写给 Node.js 等包管理器看的运行时与依赖契约,负责包级别的连接,而 tsconfig.json 则是写给 TypeScript 编译器看的类型检查与转译指令,负责代码级别的连接,下面继续分析
OpenCode
上篇 blog 提到了 tsconfig.json 的一个作用:项目引用 ,项目应用是 TypeScript 专门为 Monorepo 设计的一套机制,用来解决多个子包之间互相依赖时,如何高效进行类型检查和编译的问题 ,可以将其理解为 tsconfig.json 层面的 workspace(根目录的 package.json 才有的那个工作区)
如果没有项目引用 references ,假设现在 Monorepo 有三个包:app → core → utils(箭头表示依赖方向),当在 app 中导入 import 了 core,而 core 又导入 import 了 utils 时

- 编译器 tsc 检查上层
app时,会把core和utils的所有源码重新纳入当前编译上下文 - 改一行下层
utils的代码,三个包的类型检查都要重新跑一遍 - 包越多,编译越慢,且容易出现重复声明,类型不一致等稀奇古怪的错误(盘子大了)
有了 references 项目引用,每个包都有自己的 tsconfig.json,并通过 references 显式声明依赖关系,编译器 tsc 就能
- 独立编译 :每个包只编译自己的代码,产出
*.d.ts类型的声明文件 - 增量构建 :
utils没变(跳过),core变了(只重编core和app) - 边界清晰 :
app只能通过core的公开导出访问类型,而不能穿透到内部实现
举个例子,被依赖方,比如 packages/utils/tsconfig.json
javascript
{
"compilerOptions": {
"composite": true, // ← 🔑 关键!启用项目引用模式
"declaration": true, // ← 必须生成 .d.ts
"declarationMap": true, // ← 推荐:支持 IDE 跳转到源码
"outDir": "./dist"
},
"include": ["src"]
}
这里 composite: true 是核心开关,会告诉 tsc 编译器,这个包可以被其他项目引用,需要生成类型声明并记录构建信息,可以看到,在 OpenCode,被依赖包并不多

接下来是依赖方,比如 packages/core/tsconfig.json
javascript
{
"compilerOptions": {
"composite": true
},
"references": [
{ "path": "../utils" } // ← 声明依赖 utils
],
"include": ["src"]
}
注意,这里的 core 之所以开了 composite,是因为可能被 app 所引用(也就是它既是依赖方,又是更上层包的被依赖方),composite: true 不是引用别人的开关,而是可以被别人引用的开关
core引用了utils,core需要referencesapp引用了core,core必须能被引用,所以composite: true
所以 core 两者都需要,只有最顶层,永远不会被任何包引用的 app 应用,才可以省略 composite

【关于 composite 的含义解释】
在 composite 出现之前,TypeScript 的规则是一个 tsconfig.json 就是一个完整,不可分割的编译单元,所有文件混在一起编译,产出一堆 JS,这里 composite: true 的意思是,把这个项目变成一个标准化的预制构件,就像乐高积木或建筑中的预制板一样:
- 它有自己的标准接口(
.d.ts类型声明) - 它有自己的质量认证标记(
.tsbuildinfo构建元数据) - 它的尺寸是确定的(
rootDir约束保证输出结构稳定)
只有当一个项目被做成了这种标准预制构件(composite),别的项目才能把它当做一个整体模块安全地拼装(references)起来
OK,本篇先到这里,如有疑问,欢迎评论区留言讨论,祝各位功力大涨,技术更上一层楼!!!更多内容见下篇 blog