TypeScript 與 Node.js:ts-node 與 nodemon 的實務應用
簡介
在開發 Node.js + TypeScript 的專案時,我們常常會遇到兩個痛點:
- 每次修改程式碼後,都要重新編譯再啟動,浪費時間。
- 編譯產生的 JavaScript 檔案散落在
dist/目錄,讓除錯變得不直觀。
ts-node 與 nodemon 正是為了解決這兩個問題而誕生的工具。
ts-node讓我們可以直接在執行時 即時編譯 TypeScript,不必先產出 .js 檔。nodemon則會 監看檔案變化,自動重新啟動應用程式,讓開發流程更順暢。
結合兩者,我們能在 開發階段只需要一次指令,就完成編譯、執行、熱重載的完整流程,極大提升開發效率。
核心概念
1. 為什麼需要 ts-node?
ts-node 本質上是一個 TypeScript 執行環境,它會在 Node.js 內部呼叫 TypeScript 編譯器 (tsc) 進行即時轉譯。
主要優點:
| 優點 | 說明 |
|---|---|
| 免編譯 | 直接執行 .ts 檔,省去 tsc && node 的兩步驟 |
| 即時錯誤回報 | 編譯錯誤會即時拋出,方便除錯 |
| 支援 tsconfig | 會自動讀取專案根目錄的 tsconfig.json,保持設定一致性 |
安裝與基本使用
npm install -D ts-node typescript @types/node
# 直接執行單一檔案
npx ts-node src/index.ts
註:若專案已安裝
typescript,ts-node會自動使用相同版本的編譯器。
2. nodemon:自動重啟的守護程式
nodemon 會監看指定的檔案或目錄,一旦偵測到變更,就會 自動結束舊的 Node 進程並重新啟動。對於前端開發者來說,這就像是瀏覽器的 Hot Reload,只是發生在後端。
安裝與基本使用
npm install -D nodemon
# 監看 src 目錄下的所有檔案,執行 index.ts(需要配合 ts-node)
npx nodemon --watch src --exec "npx ts-node" src/index.ts
小技巧:將上述指令寫入
package.json的scripts,團隊成員只要執行npm run dev即可。
3. 結合 ts-node 與 nodemon:開發最佳組合
核心概念:使用
nodemon監看 TypeScript 原始碼,並透過ts-node直接執行,省去編譯步驟,同時保有熱重載功能。
範例:package.json 設定
{
"name": "ts-node-nodemon-demo",
"version": "1.0.0",
"scripts": {
"dev": "nodemon --watch src --ext ts,tsx --exec \"ts-node\" src/index.ts"
},
"devDependencies": {
"nodemon": "^3.0.1",
"ts-node": "^10.9.2",
"typescript": "^5.3.3",
"@types/node": "^20.5.1"
}
}
範例程式碼 1:簡易 HTTP Server
// src/index.ts
import http from "http";
const PORT = 3000;
const server = http.createServer((req, res) => {
res.writeHead(200, { "Content-Type": "text/plain" });
res.end("Hello from ts-node + nodemon!");
});
server.listen(PORT, () => {
console.log(`Server is running at http://localhost:${PORT}`);
});
執行
npm run dev後,打開瀏覽器http://localhost:3000,每次修改index.ts並保存,伺服器會自動重啟,立即看到變更。
範例程式碼 2:使用環境變數與 dotenv
// src/server.ts
import http from "http";
import dotenv from "dotenv";
dotenv.config(); // 讀取 .env 檔
const PORT = Number(process.env.PORT) || 4000;
const server = http.createServer((_req, res) => {
res.end(`Port from .env: ${PORT}`);
});
server.listen(PORT, () => {
console.log(`Listening on ${PORT}`);
});
# .env
PORT=5000
只要改動
.env或server.ts,nodemon都會重新啟動,且ts-node會即時讀取最新的環境設定。
範例程式碼 3:結合 Express 與路由
// src/app.ts
import express from "express";
const app = express();
app.get("/", (_req, res) => {
res.send("Home page");
});
app.get("/api/users", (_req, res) => {
res.json([{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]);
});
export default app;
// src/index.ts
import app from "./app";
const PORT = 8080;
app.listen(PORT, () => {
console.log(`Express server listening on http://localhost:${PORT}`);
});
在
package.json中的dev指令仍然只需要指向src/index.ts,nodemon會遞迴監看src目錄所有變化,無需額外設定。
範例程式碼 4:自訂 nodemon 設定檔
// nodemon.json
{
"watch": ["src"],
"ext": "ts",
"ignore": ["src/**/*.spec.ts"],
"exec": "ts-node ./src/index.ts",
"env": {
"NODE_ENV": "development"
}
}
# 只要執行
npx nodemon
這樣的設定讓 團隊成員 不必記住長指令,直接
npm run dev(或npx nodemon)即可。
4. TypeScript 編譯選項的調整
即使使用 ts-node,仍建議在 tsconfig.json 中配置以下選項,以確保程式碼在開發與正式環境都能正常運作。
{
"compilerOptions": {
"target": "ES2022",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"sourceMap": true, // 讓 stack trace 能對應回 TS 檔
"outDir": "dist"
},
"include": ["src/**/*.ts"]
}
sourceMap:即使不產出.js,ts-node仍會根據 sourceMap 顯示正確的錯誤行號。esModuleInterop:讓import express from "express"這類語法在 Node.js 中無縫運作。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方式 |
|---|---|---|
使用 ts-node 直接執行大型專案時效能下降 |
ts-node 每次執行都會即時編譯,對大量檔案會有明顯的啟動延遲。 |
開發階段使用 ts-node,正式部署時仍建議 tsc 編譯後再執行。 |
| nodemon 重啟過於頻繁 | 若監看的目錄包含 node_modules 或 dist,會造成無限重啟。 |
在 nodemon.json 或指令中 排除 這些目錄 (--ignore node_modules)。 |
| 環境變數未同步 | nodemon 重新啟動時,若 dotenv 只在程式入口載入一次,變更 .env 不會即時生效。 |
把 dotenv.config() 放在 每次程式啟動的入口,或使用 nodemon 的 --exec 參數加上 -r dotenv/config。 |
| TypeScript 路徑別名 (path alias) 無法解析 | ts-node 需要額外設定才能理解 tsconfig.json 中的 paths。 |
安裝 tsconfig-paths,並在 nodemon 指令中加入 -r tsconfig-paths/register。 |
未設定 sourceMap,除錯時只能看到編譯後的行號 |
這會讓除錯變得困難。 | 一定要開啟 sourceMap,即使使用 ts-node。 |
最佳實踐
專案結構
project/ ├─ src/ │ ├─ index.ts │ └─ ... (其他模組) ├─ .env ├─ tsconfig.json ├─ nodemon.json └─ package.json腳本統一管理
"scripts": { "dev": "nodemon", "build": "tsc", "start": "node dist/index.js" }CI/CD 中仍使用
tsc- 開發階段
npm run dev(ts-node + nodemon) - 部署前
npm run build && npm run start(編譯後執行),確保執行效能與穩定性。
- 開發階段
使用
--inspect進行遠端除錯npm run dev -- --inspectnodemon會將--inspect參數傳遞給ts-node,讓 VS Code 等 IDE 可以直接 attach。
實際應用場景
| 場景 | 為何選擇 ts-node + nodemon |
|---|---|
| 快速原型開發 | 不需要事先編譯,直接撰寫 .ts,即時看到結果。 |
| API 伺服器開發 | 需要頻繁調整路由或驗證邏輯,熱重載可省去手動重啟的時間。 |
| CLI 工具開發 | 透過 ts-node 執行指令腳本,搭配 nodemon 測試參數變化。 |
| 微服務本地測試 | 每個服務都有自己的 tsconfig,ts-node 能自動套用,nodemon 保持服務持續運行。 |
| 教學或工作坊 | 只要一條指令即可啟動環境,降低學員的環境設定門檻。 |
實務小提醒:在正式上線前,務必 關閉
ts-node,改為使用編譯後的 JavaScript,避免因即時編譯而產生的效能瓶頸與不必要的依賴。
總結
ts-node讓 TypeScript 能在 Node.js 中即時編譯執行,省去繁瑣的tsc步驟。nodemon監看檔案變化,自動重啟應用程式,提供類似前端的 Hot Reload 體驗。- 兩者結合,只要一條指令(如
npm run dev),即可完成即時編譯、熱重載與環境變數的自動載入,極大提升開發效率。 - 在開發階段使用此組合,正式部署仍建議編譯後執行,以確保效能與穩定性。
- 注意排除不必要的監看目錄、開啟
sourceMap、正確設定tsconfig,即可避免常見陷阱。
掌握了 ts-node + nodemon 的使用方式,你就能在 Node.js + TypeScript 的專案中,以更快的迭代速度、較低的錯誤率完成開發,為日後的上線與維運奠定堅實基礎。祝開發順利 🚀!