TypeScript 宣告檔案 (Declaration Files)
你一定有過這個經驗:下載了一個 npm 套件,import 進來時 TS 卻對著你大叫:
"Could not find a declaration file for module 'xxx'."
這是因為大多數的 npm 套件還是用 JavaScript 寫的,TypeScript 看不懂它們。 這時候我們需要一本說明書,告訴 TypeScript 這些 JS 程式碼到底長什麼樣子。
這本說明書就是 宣告檔案 (.d.ts)。
.d.ts 是什麼?
.d.ts 檔案只包含型別定義,不包含任何可執行的程式碼。它有點像 C 語言的 Header file (.h)。
例子:jQuery 的說明書
假設我們有一個全域變數 $ (jQuery),但 TS 不認識它。
我們可以寫一個 global.d.ts 來「宣告」它的存在:
// global.d.ts
// 告訴 TS:放心,執行環境裡會有一個叫 $ 的東西
declare var $: (selector: string) => any;
這樣 TS 就不會報錯了。
如何取得 .d.ts?
1. 套件自帶 (Bundled Types)
很多現代套件 (如 axios, vue, date-fns) 已經是用 TypeScript 寫的,或者作者在發布時順便打包了 .d.ts 檔案。
這種最爽,安裝完直接用,什麼都不用設定。
2. DefinitelyTyped (@types/xxx)
對於那些沒有附帶型別的 JS 套件 (如 express, lodash, jquery),社群好心人幫忙寫了說明書,並放在 DefinitelyTyped 這個專案裡。
你需要手動安裝它:
npm install --save-dev @types/express
npm install --save-dev @types/lodash
3. 自己寫 (Custom Declaration)
如果連 @types 都沒有,那你只能自己寫了。
在專案根目錄建立一個 types/my-module.d.ts:
// 告訴 TS 這個模組存在,甚至可以使用 lazy 宣告法:
declare module 'my-weird-library'; // 把它當成 any
// 如果你只是想讓編譯器閉嘴,不介意沒有詳細的型別提示,可以這樣寫
// 這樣一來,import 該模組時,它會被視為 any 型別
常見的 declare 語法
你會在 .d.ts 檔裡看到很多 declare 開頭的東西:
// 宣告全域變數
declare const API_URL: string;
// 宣告全域函式
declare function alert(message: string): void;
// 宣告一個模組 (例如你在 import css 檔時會用到)
declare module '*.css' {
const content: { [className: string]: string };
export default content;
}
Module Augmentation (模組擴充)
有的時候,我們不是要重新宣告一個套件,而是要擴充它現有的型別。
例如:我想在 Express 的 Request 物件上加一個 user 屬性。
我們不需要去修改 node_modules 裡的檔案,而是使用 Augmentation。
// types/express.d.ts
// 1. 必須先 import 該模組,確保它被視為模組擴充
import 'express';
// 2. 使用 declare module 擴充它
declare module 'express' {
interface Request {
user?: {
id: string;
username: string;
role: 'admin' | 'user';
};
}
}
這樣做之後,你的程式碼就可以愉快地使用 req.user 了!
Global Augmentation (全域擴充)
如果你想在 window 物件上加東西,這叫做全域擴充。
在模組化 (有 import/export) 的檔案中,你需要把宣告包在 declare global 裡。
// types/window.d.ts
export {}; // 確保這檔案被視為模組 (若檔案已有其他 import/export 則不需這行)
declare global {
interface Window {
__REDUX_DEVTOOLS_EXTENSION__: any;
analytics: {
track: (event: string) => void;
};
}
}
現在你是可以在任何地方使用 window.analytics.track('click') 了。
假如我要發布套件?
如果你在寫一個 npm 套件,記得要在 package.json 中告訴使用者你的型別檔案在哪裡:
{
"name": "my-awesome-lib",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts"
}
types(或typings) 欄位指向你的入口.d.ts檔案。
總結
.d.ts是 JS 和 TS 之間的橋樑。- Module Augmentation 讓你可以在不修改原始碼的情況下,為第三方套件「補丁」新的型別屬性。
- Global Augmentation 讓你可以安全地擴充
window或global物件。