TypeScript 宣告檔案 (Declaration Files)
宣告檔案 (Declaration Files) 是副檔名為 .d.ts 的檔案,用來為 JavaScript 程式碼提供型別資訊。它們讓 TypeScript 能夠理解和檢查 JavaScript 函式庫的型別。
為什麼需要宣告檔案?
許多流行的 JavaScript 函式庫 (如 jQuery、Lodash) 是用純 JavaScript 寫的,沒有型別資訊。宣告檔案就像是這些函式庫的「型別說明書」,讓 TypeScript 知道如何使用它們。
// 沒有宣告檔案,TypeScript 不知道 $ 是什麼
// 會報錯:Cannot find name '$'
$('.button').click();
// 有了宣告檔案,就能正確使用並獲得型別提示
$('.button').click(function (event) {
console.log(event.target);
});
安裝型別定義
@types 套件
大多數流行的函式庫都有社群維護的型別定義,放在 DefinitelyTyped 倉庫中。使用 npm 安裝:
# 安裝 jQuery 的型別定義
npm install --save-dev @types/jquery
# 安裝 Lodash 的型別定義
npm install --save-dev @types/lodash
# 安裝 Node.js 的型別定義
npm install --save-dev @types/node
安裝後,TypeScript 會自動找到並使用這些型別定義。
內建型別定義
有些套件自帶型別定義,不需要額外安裝:
# axios 自帶型別定義
npm install axios
# date-fns 自帶型別定義
npm install date-fns
撰寫宣告檔案
基本語法
宣告檔案使用 declare 關鍵字來宣告型別,而不是實作:
// globals.d.ts
// 宣告全域變數
declare const VERSION: string;
declare let DEBUG: boolean;
// 宣告全域函式
declare function greet(name: string): void;
// 宣告全域類別
declare class User {
name: string;
constructor(name: string);
greet(): void;
}
宣告模組
// lodash.d.ts
declare module 'lodash' {
export function chunk<T>(array: T[], size?: number): T[][];
export function compact<T>(array: T[]): T[];
export function concat<T>(...arrays: T[][]): T[];
// ...更多函式
}
擴增現有模組
// express-extensions.d.ts
import { Request } from 'express';
declare module 'express' {
interface Request {
user?: {
id: string;
name: string;
role: string;
};
}
}
宣告全域變數
當你使用的 JavaScript 在全域範圍定義了變數時:
// global.d.ts
// 簡單值
declare const API_URL: string;
// 物件
declare const config: {
apiUrl: string;
timeout: number;
};
// 函式
declare function analytics(event: string, data?: object): void;
// 使用全域命名空間
declare namespace MyApp {
const version: string;
function init(): void;
interface Options {
debug: boolean;
}
}
宣告類別
// my-class.d.ts
declare class MyClass {
// 屬性
readonly id: number;
name: string;
private secret: string;
// 建構子
constructor(name: string);
// 方法
greet(): string;
setName(name: string): void;
// 靜態成員
static create(name: string): MyClass;
static readonly VERSION: string;
}
宣告函式
// functions.d.ts
// 簡單函式
declare function greet(name: string): string;
// 函式重載
declare function format(value: string): string;
declare function format(value: number): string;
declare function format(value: Date): string;
// 可選參數
declare function log(message: string, level?: string): void;
// 回呼函式
declare function fetchData(url: string, callback: (error: Error | null, data: any) => void): void;
宣告介面和型別
// types.d.ts
// 介面
declare interface User {
id: number;
name: string;
email?: string;
}
// 型別別名
declare type Status = 'pending' | 'active' | 'completed';
// 泛型介面
declare interface Response<T> {
data: T;
status: number;
message: string;
}
環境宣告 (Ambient Declarations)
擴增 Window 物件
// window.d.ts
interface Window {
myApp: {
version: string;
init(): void;
};
dataLayer: any[];
gtag: (...args: any[]) => void;
}
擴充全域物件
// global.d.ts
declare global {
interface Array<T> {
customMethod(): T[];
}
interface String {
customFormat(): string;
}
}
export {}; // 確保這是一個模組
宣告檔案的結構
專案結構
project/
├── src/
│ ├── index.ts
│ └── utils.ts
├── types/
│ ├── global.d.ts
│ ├── my-library.d.ts
│ └── custom.d.ts
├── tsconfig.json
└── package.json
tsconfig.json 設定
{
"compilerOptions": {
"typeRoots": ["./types", "./node_modules/@types"],
"types": ["node", "jest"]
},
"include": ["src/**/*", "types/**/*"]
}
發佈型別定義
套件內含型別
在 package.json 中指定型別檔案:
{
"name": "my-library",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": ["dist"]
}
產生宣告檔案
在 tsconfig.json 中啟用:
{
"compilerOptions": {
"declaration": true,
"declarationDir": "./dist/types",
"emitDeclarationOnly": false
}
}
實用範例
jQuery 宣告
// jquery.d.ts
declare namespace JQuery {
interface EventObject {
target: Element;
type: string;
preventDefault(): void;
}
}
interface JQuery {
click(handler: (event: JQuery.EventObject) => void): this;
hide(): this;
show(): this;
val(): string;
val(value: string): this;
}
declare function $(selector: string): JQuery;
declare function $(element: Element): JQuery;
declare function $(callback: () => void): JQuery;
環境變數
// env.d.ts
declare namespace NodeJS {
interface ProcessEnv {
NODE_ENV: 'development' | 'production' | 'test';
API_URL: string;
SECRET_KEY: string;
}
}
圖片和樣式模組
// assets.d.ts
// 圖片
declare module '*.png' {
const value: string;
export default value;
}
declare module '*.jpg' {
const value: string;
export default value;
}
declare module '*.svg' {
const value: string;
export default value;
}
// 樣式
declare module '*.css' {
const classes: { [key: string]: string };
export default classes;
}
declare module '*.scss' {
const classes: { [key: string]: string };
export default classes;
}
Vue 單檔案元件
// vue-shim.d.ts
declare module '*.vue' {
import type { DefineComponent } from 'vue';
const component: DefineComponent<{}, {}, any>;
export default component;
}
常見問題
找不到模組的型別
// 如果沒有 @types 套件,可以自己宣告
declare module 'some-untyped-module' {
export function someFunction(): void;
export const someValue: string;
}
// 或者簡單地允許任何匯出
declare module 'some-untyped-module';
隱式 any
// tsconfig.json
{
"compilerOptions": {
"noImplicitAny": true,
"skipLibCheck": true
}
}
總結
- 宣告檔案 (
.d.ts) 為 JavaScript 程式碼提供型別資訊 - 使用
@types/*套件安裝社群維護的型別定義 - 使用
declare關鍵字宣告型別 - 可以擴充全域物件和現有模組
- 套件可以內含型別定義或由社群維護