Problem
tsconfig.json 的 include 第四條寫成:
"include": ["src/**/*.ts", "src/**/*.tsx", "scripts/**/*.ts", "tests/**/*.{ts,tsx}"]
TypeScript 的 include glob 只支援 *、?、**/,不支援大括號展開。這條 pattern 因此匹配不到任何檔案。
實測:
$ bunx tsc --noEmit --listFiles | grep -c "/tests/"
0
$ find tests -name "*.ts" | wc -l
518
最小重現(同一個 tsconfig 裡兩種寫法並列):
{"include": ["a/**/*.{ts,tsx}", "b/**/*.ts"]}
--listFiles 只列出 b/g.ts,a/f.ts 完全不在專案裡。
也就是說 bun run typecheck 綠燈從來不包含測試檔。ESLint 有跑 tests(eslint src tests scripts),所以未使用變數、implicit any 這類還擋得住,但型別錯誤沒有。
為什麼現在提
發現於 #93。當時把一個編譯期守衛(確保 write-gate.ts 的 reason 聯集與 write-gate-summary.ts 的種子清單同步)放進測試檔,mutation 檢查卻不會失敗——追下去才發現測試檔根本不在 tsc 的專案裡。守衛已改放到 src/commands/audit.ts(可以同時 import 兩邊的那一層),並實測雙向都會編譯失敗。
換句話說:任何放在測試檔裡的型別層級斷言目前都是裝飾。
修好之後的規模(已量測)
把該條改成 "tests/**/*.ts", "tests/**/*.tsx" 之後:
依錯誤碼分布:
174 TS2532 Object is possibly 'undefined'
35 TS2345 參數型別不符
27 TS18048 possibly 'undefined'
24 TS2741 缺少必要屬性
15 TS2769 多載都不符
13 TS7006 implicit any
12 TS2322 型別不可指派
9 TS2339 屬性不存在
約六成是 TS2532 / TS18048,也就是測試裡對陣列索引與 optional 欄位不做檢查就直接用(rows[0].id 這類)。這在測試裡多半是安全的——資料是自己造的——所以修起來多是加 ! 或 ?.,機械但量大。
建議做法
不要一次改完。分兩步,中間不留半綠狀態:
- 先讓 include 正確、但把測試檔隔離出去:新增
tsconfig.tests.json(extends 主檔、只 include tests/**/*.ts),並加一個 typecheck:tests script。主 typecheck 維持現狀,CI 不變。這一步 0 風險,且讓問題可量測。
- 再按目錄分批清:
tests/unit / tests/core / tests/integration / tests/gherkin 各自一個 PR,清完一批就把該目錄納入。全部清完後把 typecheck:tests 併回 typecheck,並在 CI 加上。
第一步之前不要動主 tsconfig 的 include——直接改會讓 bun run typecheck 從綠變成 341 個錯誤,等於擋住所有其他工作。
驗收
bunx tsc --noEmit --listFiles | grep -c "/tests/" 不為 0
- 放在測試檔裡的型別層級斷言(例如
const _x: Covers<A, B> = true)拿掉一個聯集成員時會編譯失敗
Problem
tsconfig.json的 include 第四條寫成:TypeScript 的 include glob 只支援
*、?、**/,不支援大括號展開。這條 pattern 因此匹配不到任何檔案。實測:
最小重現(同一個 tsconfig 裡兩種寫法並列):
{"include": ["a/**/*.{ts,tsx}", "b/**/*.ts"]}--listFiles只列出b/g.ts,a/f.ts完全不在專案裡。也就是說
bun run typecheck綠燈從來不包含測試檔。ESLint 有跑tests(eslint src tests scripts),所以未使用變數、implicit any 這類還擋得住,但型別錯誤沒有。為什麼現在提
發現於 #93。當時把一個編譯期守衛(確保
write-gate.ts的 reason 聯集與write-gate-summary.ts的種子清單同步)放進測試檔,mutation 檢查卻不會失敗——追下去才發現測試檔根本不在 tsc 的專案裡。守衛已改放到src/commands/audit.ts(可以同時 import 兩邊的那一層),並實測雙向都會編譯失敗。換句話說:任何放在測試檔裡的型別層級斷言目前都是裝飾。
修好之後的規模(已量測)
把該條改成
"tests/**/*.ts", "tests/**/*.tsx"之後:依錯誤碼分布:
約六成是
TS2532/TS18048,也就是測試裡對陣列索引與 optional 欄位不做檢查就直接用(rows[0].id這類)。這在測試裡多半是安全的——資料是自己造的——所以修起來多是加!或?.,機械但量大。建議做法
不要一次改完。分兩步,中間不留半綠狀態:
tsconfig.tests.json(extends主檔、只 includetests/**/*.ts),並加一個typecheck:testsscript。主typecheck維持現狀,CI 不變。這一步 0 風險,且讓問題可量測。tests/unit/tests/core/tests/integration/tests/gherkin各自一個 PR,清完一批就把該目錄納入。全部清完後把typecheck:tests併回typecheck,並在 CI 加上。第一步之前不要動主 tsconfig 的 include——直接改會讓
bun run typecheck從綠變成 341 個錯誤,等於擋住所有其他工作。驗收
bunx tsc --noEmit --listFiles | grep -c "/tests/"不為 0const _x: Covers<A, B> = true)拿掉一個聯集成員時會編譯失敗