ESLint, Typings, Tests 設定 0. 基本介紹 在專案開發中,統一的代碼風格、明確的型別檢查以及穩定的測試流程,能夠有效提升專案的可維護性,並避免因不同開發者的工作環境差異導致不必要的版本問題。以下是我們設定 ESLint、Typings 和測試環境的步驟。
ESLint 用來保持一致的代碼風格,避免開發過程中不同開發者之間因代碼風格不同而引起的變動。Typings 資料夾的設置是為了清晰管理型別定義,讓程式碼更加自我說明,並減少額外文件的依賴。Tests 資料夾則是專門用來撰寫測試,確保專案的每個功能都能被有效測試,避免隱藏錯誤。1. 資料夾結構 做完以後最終的資料夾結構如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 ❯ tree . ├── coverage ├── eslint.config.mjs ├── jest.config.ts ├── package.json ├── pnpm-lock.yaml ├── public │ ├── images │ ├── index.html │ ├── javascripts │ └── stylesheets │ └── style.css ├── src │ ├── app.ts │ ├── bin │ │ └── www.ts │ └── routes │ ├── index.ts │ └── users.ts ├── tests │ └── routes │ ├── index.test.ts │ └── users.test.ts ├── tsconfig.json └── typings └── routes ├── index.ts └── users.ts
2. ESLint 設定 2.1 安裝 ESLint 及相關套件 使用以下指令安裝 ESLint 和 TypeScript 相關的插件:
1 pnpm add -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-config-airbnb-base eslint-config-prettier eslint-plugin-import eslint-plugin-node eslint-plugin-prettier prettier @eslint/js typescript-eslint
2.2 初始化 ESLint 配置 使用 eslint --init
來初始化 ESLint 配置(最新版本可能與以往不同,生成 .mjs
格式的配置文件):
如果選項有所不同,以下是新的初始化步驟:
How would you like to use ESLint?
選擇:To check syntax and find problems
What type of modules does your project use?
Which framework does your project use?
Does your project use TypeScript?
Where does your code run?
ESLint 會根據這些選項生成一個新的配置文件 eslint.config.mjs
。
2.3 ESLint 配置文件 eslint.config.mjs
生成的 eslint.config.mjs
可能類似於以下內容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import globals from "globals" ;import pluginJs from "@eslint/js" ;import tseslint from "typescript-eslint" ;export default [ { files : ["**/*.{js,mjs,cjs,ts}" ] }, { languageOptions : { globals : globals.browser } }, pluginJs.configs .recommended , ...tseslint.configs .recommended , { rules : { "@typescript-eslint/no-unused-vars" : ["error" , { "argsIgnorePattern" : "^_" }] } } ];
2.4 在 package.json
中添加 ESLint 指令 在 package.json
中新增 ESLint 的執行指令:
1 2 3 "scripts" : { "lint" : "eslint 'src/**/*.ts'" }
這樣,你可以使用 pnpm run lint
來執行代碼檢查。 基本上會有兩個錯誤:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ❯ pnpm run lint > express-ts-proto@0.0.0 lint /Users/**yourname**/work/express-ts-proto > eslint 'src/**/*.ts' /Users/**yourname**/work/express-ts-proto/src/app.ts 2:10 error 'join' is defined but never used @typescript-eslint/no-unused-vars /Users/**yourname**/work/express-ts-proto/src/bin/www.ts 27:25 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any ✖ 2 problems (2 errors, 0 warnings) ELIFECYCLE Command failed with exit code 1.
join
的錯誤只需在 app.ts
中移除 join
,而另一個錯誤則需要更多的調整。
3. Error Handling 更新 3.1 更新 src/bin/www.ts
中的錯誤處理 在 src/bin/www.ts
中,將 any
修改為 NodeJS.ErrnoException
以避免使用 any
類型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 const onError = (error: NodeJS.ErrnoException ) => { if (error.syscall !== 'listen' ) { throw error; } const bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port; switch (error.code ) { case 'EACCES' : console .error (bind + ' requires elevated privileges' ); process.exit (1 ); break ; case 'EADDRINUSE' : console .error (bind + ' is already in use' ); process.exit (1 ); break ; default : throw error; } };
4. Typings 設定 4.1 建立 src/typings/routes/index.ts
和 src/typings/routes/users.ts
使用以下指令建立 src/typings
資料夾來管理路由相關的型別定義:
1 2 mkdir -p src/typings/routestouch src/typings/routes/index.ts src/typings/routes/users.ts
4.2 在 src/typings/routes/index.ts
中定義路由的 enum
1 2 3 4 5 6 export enum IndexRoutePaths { HOME = '/' , USERS = '/users' , }
4.3 在 src/typings/routes/users.ts
中定義與 users
路由相關的 enum
1 2 3 4 5 export enum UserRoutePaths { INDEX = '/' , }
4.4 確保 tsconfig.json
包含 src/typings
和 tests
在 tsconfig.json
中確認 include
部分包含 src/typings/
和 tests/
資料夾:
1 2 3 4 5 6 7 8 9 10 11 12 13 { "compilerOptions" : { "target" : "es2016" , "module" : "commonjs" , "rootDir" : "./" , "outDir" : "./dist" , "esModuleInterop" : true , "forceConsistentCasingInFileNames" : true , "strict" : true , "skipLibCheck" : true } , "include" : [ "src/**/*" , "tests/**/*" , "typings/**/*" ] }
5. Routes 設定 5.1 更新 src/routes/index.ts
在 src/routes/index.ts
中使用 enum
來定義路徑:
1 2 3 4 5 6 7 8 9 10 11 12 import { Router , Request , Response } from 'express' ;import usersRouter from './users' ;import { IndexRoutePaths } from '../typings/routes/index' ;const router = Router (); router.get (IndexRoutePaths .HOME , (req: Request, res: Response ) => { res.send ('Welcome to the Home Page' ); }); router.use (IndexRoutePaths .USERS , usersRouter);export default router;
5.2 更新 src/routes/users.ts
在 src/routes/users.ts
中使用 enum
來定義路徑:
1 2 3 4 5 6 7 8 9 10 11 import { Router , Request , Response } from 'express' ;import { UserRoutePaths } from '../typings/routes/users' ;const router = Router (); router.get (UserRoutePaths .INDEX , (req: Request, res: Response ) => { res.send ('respond with a resource' ); });export default router;
6. Jest 設定 6.1 使用 Jest 初始化 你可以使用以下指令初始化 Jest 設定:
6.2 初始化步驟 選擇以下選項來初始化 Jest 設定:
Would you like to use Jest when running “test” script in “package.json”? Would you like to use Typescript for the configuration file? Choose the test environment that will be used for testing Do you want Jest to add coverage reports? Which provider should be used to instrument code for coverage? Automatically clear mock calls, instances, contexts and results before every test? 最終,Jest 會修改 package.json
並生成 jest.config.ts
文件。
6.3 更新 jest.config.ts
以下是 Jest 的配置內容:
1 2 3 4 5 6 7 8 9 10 11 12 13 import type {Config } from 'jest' ;const config : Config = { clearMocks : true , collectCoverage : true , collectCoverageFrom : ['src/**/*.ts' ], coverageDirectory : "coverage" , coverageProvider : "v8" , preset : 'ts-jest' , testEnvironment : 'node' , };export default config;
6.4 在 package.json
中添加測試指令 在 package.json
中添加 Jest 測試指令:
1 2 3 "scripts" : { "test" : "jest" }
6.5 建立 tests
資料夾 使用以下指令建立 tests/
資料夾來撰寫測試:
1 2 3 4 mkdir testsmkdir tests/routestouch tests/routes/index.test.tstouch tests/routes/users.test.ts
6.6 撰寫測試文件 在 tests/routes/index.test.ts
中撰寫基本測試:
1 2 3 4 5 6 7 8 9 10 import request from 'supertest' ;import app from '../../src/app' ;describe ('GET /' , () => { it ('should return Welcome to the Home Page' , async () => { const res = await request (app).get ('/' ); expect (res.statusCode ).toEqual (200 ); expect (res.text ).toContain ('Welcome to the Home Page' ); }); });
在 tests/routes/users.test.ts
中撰寫基本測試:
1 2 3 4 5 6 7 8 9 10 import request from 'supertest' ;import app from '../../src/app' ;describe ('GET /users' , () => { it ('should return Welcome to the Home Page' , async () => { const res = await request (app).get ('/users' ); expect (res.statusCode ).toEqual (200 ); expect (res.text ).toContain ('respond with a resource' ); }); });
6.7 測試結果 當所有測試通過後,使用以下指令可以運行測試並檢查覆蓋率:
測試成功結果應如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 ❯ pnpm run test > express-ts-proto@0.0.0 test /Users/**yourname**/work/express-ts-proto > jest PASS tests/routes/index.test.ts GET / 200 1.224 ms - 24 PASS tests/routes/users.test.ts GET /users 200 1.116 ms - 23 ------------|---------|----------|---------|---------|------------------- File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line ------------|---------|----------|---------|---------|------------------- All files | 33.58 | 66.66 | 0 | 33.58 | src | 100 | 100 | 100 | 100 | app.ts | 100 | 100 | 100 | 100 | src/bin | 0 | 0 | 0 | 0 | www.ts | 0 | 0 | 0 | 0 | 1-89 src/routes | 100 | 100 | 100 | 100 | index.ts | 100 | 100 | 100 | 100 | users.ts | 100 | 100 | 100 | 100 | ------------|---------|----------|---------|---------|------------------- Test Suites: 2 passed, 2 total Tests: 2 passed, 2 total Snapshots: 0 total Time: 1.953 s, estimated 2 s Ran all test suites.
7 測試 www.ts
的計畫 目前還沒有針對 src/bin/www.ts
編寫測試,但未來可能會考慮撰寫相關測試來覆蓋伺服器啟動的場景。可能的測試項目包括:
伺服器成功啟動 :確認伺服器可以在指定的端口上啟動。處理端口被佔用的錯誤 :模擬 EADDRINUSE
錯誤,確保正確處理。處理權限不足的錯誤 :模擬 EACCES
錯誤,檢查應用是否正確終止。為什麼測試 www.ts
伺服器啟動流程的關鍵性 :負責 PORT 配置、HTTP 伺服器創建、錯誤處理邏輯(如 EACCES
、EADDRINUSE
)。 測試覆蓋率的完整性 :錯誤處理邏輯驗證 :為什麼不測試 www.ts
重複測試 :依賴伺服器狀態 :建議 如果伺服器啟動邏輯重要,測試錯誤處理邏輯是有價值的。 如果伺服器邏輯簡單且穩定,可以將測試重點放在應用邏輯上。 可測試的場景 伺服器成功啟動。 端口被佔用錯誤處理 (EADDRINUSE
)。 權限不足錯誤處理 (EACCES
)。 總結 測試 www.ts
能增強應用穩定性,但非必須。
8. 參考連結 參考連結