本文 AI 產出,尚未審核

TypeScript 基礎概念(Basics)

TypeScript 是什麼、與 JavaScript 的差異


簡介

在前端與 Node.js 生態系統裡,JavaScript 仍是唯一的執行語言,但隨著專案規模日益增長,純 JavaScript 的動態型別特性常常會帶來維護成本與錯誤風險。TypeScript(TS)正是為了解決這些問題而誕生的,它在 JavaScript 基礎上加入了靜態型別系統與編譯期檢查,讓開發者能在編寫程式碼時即時捕獲大部分常見錯誤。

本單元將說明 TypeScript 的定位、它與 JavaScript 的主要差異,並透過實作範例展示如何在日常開發中逐步導入型別安全。即使你是剛接觸前端的新人,或是已有多年 JavaScript 經驗的中階開發者,這篇文章都能幫助你快速掌握 TypeScript 的核心概念,為後續進階主題(如泛型、模組、裝飾器)奠定基礎。


核心概念

1. 型別系統的基本構成

類別 說明 範例
基本型別 numberstringbooleannullundefinedsymbolbigint let age: number = 30;
陣列與元組 陣列可用 type[]Array<type>;元組允許固定長度與不同型別 let nums: number[] = [1,2,3];
let point: [number, number] = [10, 20];
列舉 (enum) 為具名常數集合提供可讀性 enum Direction { Up, Down, Left, Right }
介面 (interface) 定義物件結構與合約 interface Person { name: string; age?: number; }
類別 (class) 支援 ES6 class 並加入型別檢查 class Animal { constructor(public name: string) {} }
聯合與交叉型別 ` 表示「或」;&` 表示「且」
泛型 (generic) 讓型別在使用時再決定 function identity<T>(arg: T): T { return arg; }

重點:所有 TypeScript 程式碼在執行前都會被 編譯 (transpile) 成純 JavaScript,編譯過程會檢查型別相容性,若有錯誤則會阻止產出可執行檔。

2. 型別推論 (Type Inference)

即使沒有明確寫出型別,編譯器也會根據賦值內容自動推斷。例如:

let message = "Hello TypeScript!";   // 推論為 string
const count = 5;                     // 推論為 5 (字面量型別)
let mixed = [1, "two", true];        // 推論為 (string | number | boolean)[]

技巧:在需要更嚴謹的型別時,仍建議手動加上註解,尤其是函式的參數與回傳值,這樣才能充分發揮編譯器的檢查能力。

3. 型別宣告與註解

// 基本型別宣告
let username: string = "alice";
let isAdmin: boolean = false;

// 物件型別
interface Product {
  id: number;
  name: string;
  price: number;
  tags?: string[];   // 可選屬性
}
const book: Product = {
  id: 101,
  name: "TypeScript 入門",
  price: 399,
};

4. 函式的型別檢查

// 具名函式
function add(a: number, b: number): number {
  return a + b;
}

// 匿名函式 (箭頭函式) + 型別推論
const greet = (name: string): void => {
  console.log(`Hello, ${name}!`);
};

注意:若省略回傳型別,編譯器會根據 return 表達式自動推論。但在較複雜的函式(例如多條件回傳)時,手動標註回傳型別能避免意外的 any 推論。

5. anyunknown

型別 說明 何時使用
any 完全放棄型別檢查,等同於 JavaScript 原始行為 快速遷移大型 JS 專案時的暫時措施
unknown 必須先做型別檢查才能使用,較安全的 any 替代 接收外部 API、JSON 解析等不確定資料時
let data: unknown = JSON.parse('{"x":10}');
if (typeof data === "object" && data !== null && "x" in data) {
  const x = (data as { x: number }).x; // 必須斷言
  console.log(x);
}

6. 型別斷言 (Type Assertion)

有時候我們確定某個變數的實際型別,卻因為編譯器無法推斷,需要手動告訴編譯器:

let el = document.getElementById("app") as HTMLDivElement;
el.style.backgroundColor = "lightblue";

警告:過度使用斷言會削弱 TypeScript 的安全性,僅在真的無法讓編譯器自行推斷時才使用。


程式碼範例

下面提供 5 個實務上常用 的 TypeScript 範例,從基本型別到泛型都有涵蓋,並在每段程式碼後加入說明。

範例 1:基本型別與函式

// 1️⃣ 基本型別宣告
let id: number = 12345;
let title: string = "TypeScript 入門";
let isPublished: boolean = true;

// 2️⃣ 函式簽名
function formatTitle(id: number, title: string): string {
  return `[${id}] ${title}`;
}

// 3️⃣ 呼叫
const result = formatTitle(id, title);
console.log(result); // [12345] TypeScript 入門

說明:這段程式展示了 型別註解函式參數與回傳值 的寫法,編譯器會保證 id 必須是 number,若傳入字串會在編譯階段報錯。


範例 2:介面與可選屬性

interface User {
  id: number;
  name: string;
  email?: string; // 可選屬性
}

// 建立符合介面的物件
const alice: User = {
  id: 1,
  name: "Alice",
  email: "alice@example.com",
};

const bob: User = {
  id: 2,
  name: "Bob", // 沒有 email 也是合法的
};

說明email? 表示此屬性可以不存在,使用介面可以在大型專案中統一物件結構,減少錯字或遺漏欄位的問題。


範例 3:列舉 (Enum) 與字串映射

enum Role {
  Admin = "ADMIN",
  Editor = "EDITOR",
  Viewer = "VIEWER",
}

function canEdit(role: Role): boolean {
  return role === Role.Admin || role === Role.Editor;
}

// 測試
console.log(canEdit(Role.Admin));   // true
console.log(canEdit(Role.Viewer));  // false

說明:列舉讓 常數值 具備可讀性,同時在編譯期就能限制只能使用列舉成員,避免硬編碼字串導致的錯誤。


範例 4:泛型函式 – 取得陣列第一個元素

function firstElement<T>(arr: T[]): T | undefined {
  return arr.length > 0 ? arr[0] : undefined;
}

// 使用不同型別的陣列
const num = firstElement([10, 20, 30]);          // num: number | undefined
const str = firstElement(["a", "b", "c"]);      // str: string | undefined
const mixed = firstElement([1, "two", true]);   // mixed: number | string | boolean | undefined

說明<T> 表示 泛型參數,在呼叫時會根據實際傳入的陣列自動推斷 Tnumberstring 或其他型別,使函式具備 高度重用性


範例 5:型別守護 (Type Guard) 與 unknown

function isString(value: unknown): value is string {
  return typeof value === "string";
}

function printLength(value: unknown) {
  if (isString(value)) {
    // 在此分支中,value 已被縮窄為 string
    console.log(`長度:${value.length}`);
  } else {
    console.log("不是字串,無法取得長度");
  }
}

// 測試
printLength("Hello");   // 長度:5
printLength(123);       // 不是字串,無法取得長度

說明value is string型別守護,讓編譯器在條件分支內推斷 valuestring,從而安全存取 .length。這是處理 unknown 或外部資料的常見技巧。


常見陷阱與最佳實踐

陷阱 描述 最佳實踐
過度使用 any 失去型別檢查的好處,等同回到純 JavaScript。 盡量在 tsconfig.json 中開啟 noImplicitAny,僅在最後一步才允許 any
忽略 strictNullChecks null / undefined 會被視為所有型別的子類別,容易產生執行時錯誤。 啟用 strictNullChecks,在變數上明確標註 `
型別斷言濫用 直接把 any 斷言成目標型別,會讓錯誤在執行階段才顯現。 只在確定資料來源可靠且編譯器無法推斷時使用,並配合跑測試。
未設定 tsconfig.json 預設編譯選項過於寬鬆,易忽略潛在問題。 建立嚴格模式的 tsconfig.jsonstrict: truenoImplicitAnynoUnusedLocals 等)。
相容性問題 直接引用第三方未提供型別宣告的 JavaScript 套件會產生 any 使用 @types 社群套件或自行撰寫宣告檔 (.d.ts)。

建議的 tsconfig.json 範本

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "strict": true,                     // 開啟所有嚴格檢查
    "noImplicitAny": true,
    "strictNullChecks": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "sourceMap": true,
    "outDir": "./dist"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

實際應用場景

  1. 前端框架 (React / Vue / Angular)

    • 使用 TypeScript 能為 Component PropsStateHooks 提供完整型別,減少 UI 變更時的破壞性錯誤。
    • 例如在 React 中,useState<number>(0) 明確指出狀態只能是 number,避免因不小心寫成字串導致渲染失敗。
  2. Node.js 後端服務

    • Express、Koa 等框架的請求/回應物件可透過介面描述,使路由處理函式的參數型別一致。
    • 結合 ORM(如 TypeORM)時,資料模型本身即是 TypeScript 類別,編譯期即可檢查欄位名稱與型別。
  3. 大型單頁應用 (SPA) 或微前端

    • 隨著程式碼量增長,跨模組的型別共享(透過 export typedeclare module)可避免不同團隊之間的型別不一致。
  4. 開發套件與函式庫

    • 若你要發佈 npm 套件,提供 .d.ts 型別宣告會大幅提升使用者體驗,讓使用者在 IDE 中即能得到自動完成與錯誤提示。
  5. 與第三方 API 的互動

    • 透過 fetch 取得的 JSON 資料往往是 any,使用 型別守護zod / yup 等驗證庫可在取得資料後立即轉換為安全型別。

總結

  • TypeScriptJavaScript 的超集,透過靜態型別、編譯期檢查與豐富的語法特性,讓開發者在寫程式時就能捕捉多數錯誤。
  • 與純 JavaScript 的差異核心在於 型別系統編譯階段的嚴格檢查,以及 工具鏈支援(如 tsc、ESLint、IDE 智慧提示)。
  • 本文介紹了 基本型別、介面、列舉、泛型、型別守護 等概念,並提供 5 個實務範例,示範如何在日常開發中運用 TypeScript。
  • 為避免常見陷阱,建議在專案一開始就設定 嚴格模式strict: true),盡量減少 any、善用 unknown 與型別守護,並配合 單元測試 形成雙重防護。
  • 無論是前端框架、Node.js 服務、或是開發公共套件,TypeScript 都能提升程式碼的可讀性、可維護性與可靠性。

掌握了以上基礎,你已經具備在任何 JavaScript 專案中安全導入 TypeScript 的能力。接下來可以進一步探索 模組系統、命名空間、進階泛型、映射型別 等更高階的主題,讓你的程式碼品質持續向上提升。祝你在 TypeScript 的學習旅程中玩得開心、寫得安心!