剛寫 TS 的時候,我看到專案裡莫名其妙冒出個 types.d.ts
或者 global.d.ts
,心裡是有點懵的。啥?這個文件是幹啥的?我 .ts
文件不是已經寫類型了嗎?為什麼還要多此一舉?
後來,專案越做越大,第三方庫用得越來越多,加上團隊裡有人用 JS、有人用 TS,我才發現——.d.ts
文件,其實是個“救火隊員”。
.d.ts
?先說個場景。
我們團隊之前接入了一個老系統,人家封裝了一個 JS 工具庫,叫 legacy-utils.js
,裡面一堆函數:
// legacy-utils.js
function formatDate(date) {
return date.toISOString().slice(0, 10);
}
function calculateTax(amount, rate) {
return amount * rate * 0.01;
}
我們在 TS 專案裡直接 import:
import { formatDate } from './legacy-utils';
formatDate(new Date()); // 報錯:Could not find a declaration file...
紅了! VSCode 一下就給你標紅,說“這玩意沒類型,我沒法檢查”。
這時候,你有兩個選擇:
.ts
—— 別想,那是別的團隊維護的。.d.ts
文件,告訴 TypeScript:“別慌,我知道它有啥類型。”於是,我默默在專案裡建了個 types/legacy-utils.d.ts
:
// types/legacy-utils.d.ts
declare module 'legacy-utils' {
export function formatDate(date: Date): string;
export function calculateTax(amount: number, rate: number): number;
}
然後,在 tsconfig.json
裡確保 typeRoots
包含了 types
目錄。
再回到程式碼裡,紅波浪線沒了,自動補全也有了,世界清淨了。
那一刻我悟了:
.d.ts
就是給 JS 打“類型補丁”的。
.d.ts
來兜底有些老專案,喜歡把變數掛到 window
上:
// index.html
<script>
window.APP_CONFIG = { apiUrl: 'https://api.example.com' };
</script>
你在 TS 裡寫:
console.log(window.APP_CONFIG.apiUrl); // 類型“Window & typeof globalThis”上不存在屬性“APP_CONFIG”
煩不煩?煩。
解決方法:建個 global.d.ts
:
// global.d.ts
interface Window {
APP_CONFIG: {
apiUrl: string;
};
}
保存,刷新,紅波浪線消失。舒服了。
我管這個叫“強行擴展”,雖然有點野路子,但專案要上線,誰還管你是不是優雅。
比如你用了某個小眾 npm 包,叫 super-fast-hash
,作者沒寫類型,但你又不想用 any
(畢竟開了 noImplicitAny
)。
你可以:
// types/super-fast-hash.d.ts
declare module 'super-fast-hash' {
const hash: (input: string) => string;
export default hash;
}
然後你就可以:
import hash from 'super-fast-hash';
const result = hash('hello'); // 類型正確,不報錯
雖然這個庫可能就用一次,但至少程式碼看起來“專業”了點,對吧?
比如我們專案裡經常用到一種“用戶狀態”:
type UserStatus = 'active' | 'inactive' | 'pending';
如果每個文件都 import,太麻煩。不如:
// types/global-types.d.ts
type UserStatus = 'active' | 'inactive' | 'pending';
然後在 tsconfig.json
裡加:
{
"compilerOptions": {
"typeRoots": [
"node_modules/@types",
"types"
]
}
}
這樣,所有 .ts
文件裡都能直接用 UserStatus
,不用 import。
是不是有點“全局污染”?是。但小專案圖個省事。
.d.ts
文件的潛規則文件名無所謂,但最好有意義
比如 axios.d.ts
、env.d.ts
,一看就知道是幹啥的。
內容只能是類型相關
你不能在 .d.ts
裡寫 const x = 1
,會報錯。它只能有 type
、interface
、declare
這些。
declare module 是“聲明模塊”的萬能鑰匙
第三方庫沒類型?用它!JS 文件想加類型?用它!
別濫用,小心“類型幻覺”
你寫了個 declare const api: any;
,確實不報錯了,但等於啥也沒做。類型檢查形同虛設,別騙自己。
.d.ts
文件用得好,它讓你的專案更健壯;用得爛,它讓你的類型系統變成“皇帝的新衣”。
(寫完這篇,我回頭看了看專案裡的十幾個 .d.ts
文件,嘆了口氣:是時候重構了……)