上一篇我們已經建立好 React + TypeScript 的新專案環境了,但這還只是開始😌。
這篇會是初始化專案的重要關鍵,我們要來聊聊 Vite 預設專案裡尚未完善的設定,包含 TypeScript 與 ESLint 的 config。由於網路上多數 ESLint 文章仍停留在 9.0 以前的版本,這篇能幫助正在學習的你少走彎路。
本篇重點整理:
tsconfig 設定.eslintrc vs eslint.config.js)tsconfig.* 與 eslint.config.js
TypeScript 在網路或歷屆鐵人賽文章中已有大量資源,在這裡我不會贅述太多底層細節,但會在底下附上詳細的參考資料與文章連結,提供給正在學習的各位。
其實 TypeScript 的 預設 config 就已經能夠應付大多數開發需求了。
但在開始調整設定前,我想先補充上一篇沒有提到的工具,同時也是這一次的主題:
這時你會問
我已經有 ESLint 了,怎麼還需要 TypeScript ?
兩者其實角色不同:
function add(a: number, b: number) {
  return a + b;
}
add(1, "2"); // ❌ TypeScript 會報錯,字串 "2" 與 型別:number 不一致
const unusedVar = 123; // ⚠️ ESLint 會警告有未使用的變數
function greet(name: string) {
  console.log(Name); // ⚠️ ESLint 會警告未定義的變數
}
既然我們已經大致了解了 TypeScript,接下來就來看看一些常見的 config 設定吧!
在建立好專案環境後,通常會看到幾個與 TypeScript 有關的檔案:
tsconfig.json — 專案通用的基本設定tsconfig.app.json — 針對前端應用程式程式碼的設定tsconfig.node.json — 針對 Node.js 環境程式碼的設定 ( 如 vite.config.ts )vite-env.d.ts — 用來補充 .env 全域型別下面整理了一些常見的 tsconfig 設定與用途:
"target": "ES2022",                 // 編譯目標 JS 版本
"module": "ESNext",                 // 使用 ES 模組
"moduleResolution": "Bundler",      // 適合 Vite / ESBuild 的模組解析方式
"strict": true,                     // 啟用嚴格檢查
"jsx": "react-jsx",                 // JSX 轉譯,使用 React 17+ JSX runtime
"isolatedModules": true,            // 每個檔案可單獨編譯
"noEmit": true,                     // 只檢查,不輸出檔案
"skipLibCheck": true                // 跳過 node_modules 型別檢查,加快編譯速度
在創建新專案後,你應該會發現 eslint.config.js 的檔案。
不過在網路上有許多不同版本的資料,找資料時很容易混淆。這裡我們簡單帶大家了解新舊版本的差異。
ESLint 9.0 以前
.eslintrc(可能是 .json 或 .js 等格式)來設定規則module.exports = {
  parser: '@typescript-eslint/parser',
  extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
  rules: {
    // 自訂規則
  },
}
新一代 ESLint (9.x 以上)
eslint.config.js 作為統一入口import { defineConfig } from 'eslint-define-config'
export default defineConfig({
  parser: '@typescript-eslint/parser',
  extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
  rules: {
    // 自訂規則
  },
})
Prettier 建議直接安裝 VSCode 擴充套件,存檔即可自動整理程式碼,方便又快速
當你在 Vite 專案中開發一段時間後,可能會發現一個問題:
ESLint 沒有即時運作,每次都得手動執行
npm run lint,甚至要等到 CI/CD 階段才發現一大堆錯誤。
其實這不是 ESLint 壞掉,而是 Vite 預設並沒有幫你把 ESLint 與編輯器完全接上線 😔。
在這裡,我們將這個問題給修復。
安裝 ESLint 的 VSCode 擴充套件
按下 F1 或 ctrl + shift + P 輸入 open settings,選擇 Open User Settings (JSON)
在 setting.json 原有的程式底下加入以下設定
  "eslint.validate": [            // 將以下檔案類型套用 ESLint 檢查
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact"
  ],
  "eslint.useFlatConfig": true    // 啟用 ESLint v9 的模式
                                  // false 則為舊式的 .eslintrc
點開專案內的 App.tsx ,你應該能即時看到 ESLint 報錯
在往後開啟新專案後,ESLint 就能即時幫你偵測並提醒錯誤,不需要再手動跑 npm run lint 了。
到這裡,TypeScript 與 ESLint 的基礎設定就大功告成🎉!
接下來我們就能專心在程式功能的開發上,讓環境本身成為默默守護程式品質的後盾🚀
下載開發依賴套件
npm i -D eslint-plugin-react @typescript-eslint/eslint-plugin @typescript-eslint/parser
tsconfig.json
{
  "compilerOptions": {
    // 編譯 & 模組設定
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "useDefineForClassFields": true,
    "verbatimModuleSyntax": true,
    "moduleDetection": "force",
    "allowImportingTsExtensions": true,
    "jsx": "react-jsx",
    "noEmit": true,
    "skipLibCheck": true,
    "isolatedModules": true,
    // 嚴格檢查 & linting
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedSideEffectImports": true,
    "erasableSyntaxOnly": true
  }
}
tsconfig.app.json
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022", "DOM", "DOM.Iterable"],
    "types": ["vite/client"],
    "composite": true,
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo"
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}
tsconfig.node.json
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "target": "ES2023",
    "lib": ["ES2023"],
    "types": ["node"],
    "composite": true,
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo"
  },
  "include": ["vite.config.ts", "scripts", "tools"],
  "exclude": ["node_modules", "dist", "src"]
}
eslint.config.js
import js from "@eslint/js";
import globals from "globals";
import react from "eslint-plugin-react";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import tsParser from "@typescript-eslint/parser";
import tsPlugin from "@typescript-eslint/eslint-plugin";
import { defineConfig } from "eslint/config";
export default defineConfig([
  {
    ignores: ["dist/**"],
  },
  {
    files: ["**/*.{ts,tsx}"],
    plugins: {
      react,
      "react-hooks": reactHooks,
      "react-refresh": reactRefresh,
      "@typescript-eslint": tsPlugin,
    },
    languageOptions: {
      parser: tsParser,
      globals: {
        ...globals.browser,
        ...globals.node,
        "import.meta": "readonly",
      },
      parserOptions: {
        // 使 @typescript-eslint 理解 tsconfig.json 的設定,執行更精確的檢查
        project: "./tsconfig.json",
      },
    },
    settings: {
      react: { version: "detect" },
    },
    rules: {
      ...react.configs.recommended.rules,
      ...react.configs["jsx-runtime"].rules,
      ...reactHooks.configs.recommended.rules,
      ...tsPlugin.configs.recommended.rules,
      "react-refresh/only-export-components": "warn",
      // 關閉 ESLint 原生規則,改用 TS plugin
      "no-unused-vars": "off",
      "@typescript-eslint/no-unused-vars": [
        "warn",
        { varsIgnorePattern: "^_", argsIgnorePattern: "^_" },
      ],
      // TypeScript 專案可以關掉 prop-types
      "react/prop-types": "off",
    },
  },
]);
參考資料 & 文章分享:
TypeScript: 語法 & config 設定
ESLint:config 設定