0%

吐槽 TypeScript Handbook

It Says:

Referenced projects must have the new composite setting enabled. This setting is needed to ensure TypeScript can quickly determine where to find the outputs of the referenced project. Enabling the composite flag changes a few things:

See https://www.typescriptlang.org/docs/handbook/project-references.html#composite

截图

Real Usage: crate-vite

可见 tsconfig.app.json / tsconfig.node.json 被 reference, 但没有 composite, 尝试了 tsc --noEmit 进行 typecheck 也没有问题

So, what’s wrong ?

不明所以, 只好求助 ChatGPT

实际行为: 没有开启 –build 模式

TypeScript Handbook 中所说的 references 是为 Project References(项目构建系统) 服务的,而项目引用机制的完整行为(例如强制要求 composite: true)只有在执行:

1
2
3
tsc --build
# 或简写
tsc -b

的时候才会启用

然后我继续追问:

我又重新阅读了一遍 TypeScript 官方的 handbook, 并没有提到仅限 build mode 需要 composite

Referenced projects must have the new composite setting enabled. This setting is needed to ensure TypeScript can quickly determine where to find the outputs of the referenced project. Enabling the composite flag changes a few things:

所以是官方 HandBook 不严谨么

回复

💡 为什么官方这么写?

TypeScript 的 Handbook 是面向“使用 project references 来实现模块化编译”的用例写的,也就是 tsc -b 的场景。而像 Vite 这类现代前端工具根本不走 tsc 构建,而是仅利用 TypeScript 做类型检查,因此 composite 的要求自然也就不适用了。

但文档没有把这个限定条件讲清楚,这是官方文档一个很典型的“行为没错,表述太武断”问题。社区里其实也有很多人吐槽这个点。

create-vite 的更新

create-vite 早期版本是 tsconfig.json + tsconfig.node.json

  • tsconfig.json 包含 src App 的配置, 并 reference tsconfig.node.json
  • tsconfig.node.json 被 reference, 并且 composite:true

通过 https://github.com/vitejs/vite/pull/17774 可以得知是

composite is not required when the project by referenced an empty project

实验下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"references": [
{ "path": "./tsconfig.app.json" }, // FE App
{ "path": "./tsconfig.vite.json" } // Vite config
// { "path": "./tsconfig.node.json" } // other config runs in node
],
"include": ["uno.config.ts", "eslint.config.js"],
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"module": "NodeNext",
"moduleResolution": "nodenext",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"strict": true,
"allowJs": true
}
}

tsc --noEmit 仍然要求 composite, ChatGPT 你就胡编吧 😂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
_typecheck: tsconfig.json:4:5 - error TS6306: Referenced project '/some-dir/tsconfig.app.json' must have setting "composite": true.
_typecheck:
_typecheck: 4 { "path": "./tsconfig.app.json" }, // FE App
_typecheck: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
_typecheck:
_typecheck: tsconfig.json:4:5 - error TS6310: Referenced project '/some-dir/tsconfig.app.json' may not disable emit.
_typecheck:
_typecheck: 4 { "path": "./tsconfig.app.json" }, // FE App
_typecheck: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
_typecheck:
_typecheck: tsconfig.json:5:5 - error TS6306: Referenced project '/some-dir/tsconfig.vite.json' must have setting "composite": true.
_typecheck:
_typecheck: 5 { "path": "./tsconfig.vite.json" } // Vite config
_typecheck: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
_typecheck:
_typecheck: tsconfig.json:5:5 - error TS6310: Referenced project '/some-dir/tsconfig.vite.json' may not disable emit.
_typecheck:
_typecheck: 5 { "path": "./tsconfig.vite.json" } // Vite config
_typecheck: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

当我询问在 TypeScript src 哪里抛出的错误, 以及 empty project(或者叫 resolution project) 不限制 composite 时, ChatGPT / 最新的 Qwen3 / Deepseek 都失败了, 效果 Deepseek > Qwen3 > ChatGPT


最终在 Deepseek 的提示下, 根据

1
2
3
4
5
6
7
// src/compiler/diagnosticMessages.json
{
"code": 6306,
"category": "Error",
"key": "Referenced_project_0_must_have_setting_composite_true",
"message": "Referenced project '{0}' must have setting 'composite': true."
}

搜索 Referenced_project_0_must_have_setting_composite_true 找到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// https://github.com/microsoft/TypeScript/blob/v5.8.3/src/compiler/program.ts#L4611-L4618
if (!options.composite || options.noEmit) {
// ok to not have composite if the current program is container only
const inputs = parent ? parent.commandLine.fileNames : rootNames
if (inputs.length) {
if (!options.composite)
createDiagnosticForReference(
parentFile,
index,
Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true,
ref.path,
)
if (options.noEmit)
createDiagnosticForReference(parentFile, index, Diagnostics.Referenced_project_0_may_not_disable_emit, ref.path)
}
}

More

因为误导的地方太多, 加上之前踩得一些坑, 不得不吐槽…

Module Augmentation v.s Ambient module declaration

槽点:

关于这两个名词(行为)的区别, Handbook 隐藏的太深, 它们又很相似, 谁能想到一个 export {} 会有这么大区别?

more

to be continued…