本文 AI 產出,尚未審核

TypeScript 課程 ── 型別宣告與整合

主題:DefinitelyTyped 套件安裝(@types/


簡介

在使用 TypeScript 開發前端或 Node.js 專案時,常會需要 第三方 JavaScript 套件(例如 lodash、moment、express 等)。這些套件本身是以純 JavaScript 撰寫,沒有內建的型別資訊,若直接在 TypeScript 中引用,編譯器將無法提供型別檢查與 IntelliSense,開發體驗會大打折扣。

為了解決這個問題,社群建立了 DefinitelyTyped 專案,集中管理超過 20,000 個套件的型別宣告檔 (*.d.ts)。開發者只要透過 npm 安裝對應的 @types/ 套件,即可讓 TypeScript 立即取得完整的型別資訊,提升開發效率與程式品質。

本篇文章將帶你 一步一步安裝與使用 @types/ 套件,並說明常見的陷阱與最佳實踐,讓你在實務專案中快速上手型別整合。


核心概念

1. DefinitelyTyped 與 @types/ 套件的關係

  • DefinitelyTyped:一個開源倉庫,收錄各種 JavaScript 套件的型別宣告。
  • @types/:npm 上的發佈套件,名稱皆以 @types/ 為前綴,代表「從 DefinitelyTyped 取得的型別定義」。
  • 安裝方式npm i -D @types/<package-name>-D 代表開發依賴)。

:若套件本身已內建型別(如 reactaxios),則不需要額外安裝 @types/

2. 安裝前的檢查

在安裝 @types/ 前,先確認套件的 版本相容性

# 查看已安裝套件的版本
npm list lodash
# 取得對應的型別套件
npm view @types/lodash version

@types/lodash 的版本較舊,可能缺少新功能的型別,這時可以直接指定相同的主版本號:

npm i -D @types/lodash@^4.14.0

3. TypeScript 設定 (tsconfig.json)

tsconfig.json 中的 typeRootstypes 欄位可控制型別搜尋範圍。

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es6",
    "strict": true,
    "typeRoots": ["./node_modules/@types"],   // 預設搜尋路徑
    "types": ["node", "lodash"]               // 只載入指定的型別套件
  }
}

小技巧:若專案只需要少數型別,設定 types 能減少編譯時間與衝突機會。

4. 程式碼範例

以下示範三個常見套件的型別安裝與使用方式。

範例 1:Lodash

npm i lodash
npm i -D @types/lodash
import _ from "lodash";

// 使用 _.chunk 時,IDE 會顯示完整的參數型別
const numbers = [1, 2, 3, 4, 5, 6];
const chunked = _.chunk(numbers, 2); // => [[1,2],[3,4],[5,6]]

範例 2:Moment.js

npm i moment
npm i -D @types/moment
import moment from "moment";

const now = moment();                     // now: Moment
const formatted = now.format("YYYY-MM-DD"); // 取得字串型別
// 錯誤寫法:now.format(123); // 編譯時會直接報錯

範例 3:Express(Node.js)

npm i express
npm i -D @types/express
import express, { Request, Response } from "express";

const app = express();

app.get("/hello", (req: Request, res: Response) => {
  // req.query 的型別會自動推斷為 string | string[] | undefined
  const name = req.query.name ?? "World";
  res.send(`Hello, ${name}`);
});

app.listen(3000, () => console.log("Server running on http://localhost:3000"));

範例 4:自訂全域型別(若找不到官方宣告)

# 假設使用的套件沒有 @types
npm i some-legacy-lib
// 在 src/types/global.d.ts 中自行宣告
declare module "some-legacy-lib" {
  export function legacyFunc(param: string): number;
}
import { legacyFunc } from "some-legacy-lib";

const result = legacyFunc("test"); // result: number

範例 5:使用 skipLibCheck 減少型別衝突

{
  "compilerOptions": {
    "skipLibCheck": true   // 跳過所有宣告檔的型別檢查
  }
}

說明:在大型專案中,若某些 @types/ 套件的型別與自訂型別衝突,skipLibCheck 能暫時讓編譯通過,但應盡量避免長期使用。


常見陷阱與最佳實踐

陷阱 說明 解決方案
安裝錯誤的版本 @types/ 版本與實際套件不匹配,導致缺少或錯誤的型別 先確認套件的 major 版本,使用 npm view @types/<pkg> version,或加上 @<major> 指定
重複的全域型別 同時安裝多個相同名稱的型別套件(例如 @types/node 與某套件內建) 只保留一個來源;若衝突,使用 typeRootspaths 排除
忘記 -D @types/ 套件安裝成 production 依賴,造成最終產出檔案過大 使用 npm i -D @types/...,或在 package.json 中手動移到 devDependencies
未設定 esModuleInterop 某些 @types 需要 esModuleInterop:true 才能正確 import tsconfig.json 中加上 "esModuleInterop": true
過度依賴 any 為了快速跑通程式,直接把型別宣告為 any,失去型別保護 盡量使用 unknown 或自行擴充型別,保持 TypeScript 的安全性

最佳實踐

  1. 先搜尋官方型別:在 npm 上先查 package-name,若有 @types/,直接安裝;若套件已內建型別,則不必額外安裝。
  2. 使用 --save-dev:所有 @types/ 均應視為開發依賴。
  3. 定期更新:使用 npm outdated 檢查型別套件的最新版本,避免因舊版型別導致錯誤。
  4. 在 CI 中加入型別檢查:確保每次提交都會執行 tsc --noEmit,防止型別衝突被忽略。

實際應用場景

  1. 前端 React 專案
    • 安裝 @types/react@types/react-dom,讓 JSX 元素得到完整型別支援。
  2. Node.js 後端 API
    • 使用 expressmongoose,分別安裝 @types/express@types/mongoose,確保路由與資料模型的型別正確。
  3. 測試框架
    • Jest、Mocha 等測試工具需要 @types/jest@types/mocha,才能在測試檔案中使用 expectdescribe 等全域函式而不產生錯誤。
  4. 舊有 JavaScript 套件
    • 當專案需要引入未提供型別的舊套件(例如 underscore 的舊版),只要安裝 @types/underscore,即能在 TypeScript 中安全使用。

總結

DefinitelyTyped 為 TypeScript 生態系提供了 龐大的型別資源庫,只要透過 npm i -D @types/<package>,即可讓原本只有 JavaScript 的套件瞬間具備完整的型別支援。
本文從 安裝前的版本檢查tsconfig.json 設定實務範例,到 常見陷阱與最佳實踐,一步步說明了如何在專案中正確整合 @types/ 套件。

在日常開發中,養成定期更新型別套件、在 CI 中執行型別檢查 的好習慣,能最大化 TypeScript 的安全優勢,讓程式碼更可靠、更易維護。祝你在 TypeScript 的旅程中,玩得開心、寫得更好!