TypeScript 型別別名 (Type Alias)

型別別名 (Type Alias) 使用 type 關鍵字為型別建立一個新名稱。它可以用來簡化複雜的型別定義,提高程式碼的可讀性。

基本語法

type TypeName = Type;

原始型別別名

type ID = string;
type Age = number;
type IsActive = boolean;

let userId: ID = 'user-123';
let userAge: Age = 25;
let active: IsActive = true;

物件型別別名

type User = {
  name: string;
  age: number;
  email?: string;
};

let user: User = {
  name: '小明',
  age: 25,
};

function printUser(user: User): void {
  console.log(`${user.name}, ${user.age} 歲`);
}

聯合型別 (Union Types)

型別別名特別適合定義聯合型別:

type StringOrNumber = string | number;
type Status = 'pending' | 'active' | 'completed';
type ID = string | number;

let value: StringOrNumber = 'hello';
value = 42; // OK

let status: Status = 'active';
// status = "unknown";  // 錯誤

function processId(id: ID): void {
  if (typeof id === 'string') {
    console.log(id.toUpperCase());
  } else {
    console.log(id.toFixed(2));
  }
}

交集型別 (Intersection Types)

使用 & 組合多個型別:

type Named = { name: string };
type Aged = { age: number };
type Person = Named & Aged;

let person: Person = {
  name: '小明',
  age: 25,
};

// 組合介面和型別
interface HasId {
  id: number;
}

type IdentifiedPerson = Person & HasId;

let identifiedPerson: IdentifiedPerson = {
  id: 1,
  name: '小華',
  age: 30,
};

函式型別別名

type MathOperation = (a: number, b: number) => number;

let add: MathOperation = (a, b) => a + b;
let multiply: MathOperation = (a, b) => a * b;

// 帶有屬性的函式型別
type NamedFunction = {
  (x: number): number;
  name: string;
};

元組型別別名

type Point = [number, number];
type RGB = [number, number, number];
type NameAge = [string, number];

let origin: Point = [0, 0];
let red: RGB = [255, 0, 0];
let person: NameAge = ['小明', 25];

// 具名元組
type Coordinate = [x: number, y: number, z?: number];

泛型型別別名

type Container<T> = {
  value: T;
};

type Nullable<T> = T | null;
type Optional<T> = T | undefined;
type Maybe<T> = T | null | undefined;

let box: Container<string> = { value: 'hello' };
let maybeNumber: Nullable<number> = 42;
maybeNumber = null; // OK

條件型別

type NonNullable<T> = T extends null | undefined ? never : T;

type A = NonNullable<string | null>; // string
type B = NonNullable<number | undefined>; // number

遞迴型別別名

型別別名可以參照自身:

// 樹狀結構
type TreeNode = {
  value: string;
  children?: TreeNode[];
};

let tree: TreeNode = {
  value: 'root',
  children: [
    { value: 'child1' },
    {
      value: 'child2',
      children: [{ value: 'grandchild' }],
    },
  ],
};

// JSON 值
type JSONValue = string | number | boolean | null | JSONValue[] | { [key: string]: JSONValue };

模板字面量型別

TypeScript 4.1+ 支援模板字面量型別:

type EventName = 'click' | 'focus' | 'blur';
type EventHandler = `on${Capitalize<EventName>}`;
// "onClick" | "onFocus" | "onBlur"

type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type ApiEndpoint = `/api/${string}`;
type ApiRoute = `${HttpMethod} ${ApiEndpoint}`;
// "GET /api/..." | "POST /api/..." | ...

從值提取型別

typeof

let user = {
  name: '小明',
  age: 25,
  isActive: true,
};

type User = typeof user;
// { name: string; age: number; isActive: boolean }

const colors = ['red', 'green', 'blue'] as const;
type Color = (typeof colors)[number]; // "red" | "green" | "blue"

keyof

type User = {
  name: string;
  age: number;
  email: string;
};

type UserKey = keyof User; // "name" | "age" | "email"

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

型別別名 vs 介面

特性Type AliasInterface
物件型別
聯合型別
交集型別✓ (用 &)用 extends
元組可以但較繁瑣
原始型別
宣告合併
extends
implements✗ 直接使用

何時使用 type

  • 定義聯合型別
  • 定義元組型別
  • 為原始型別建立別名
  • 使用映射型別或條件型別
  • 需要從現有型別提取型別

何時使用 interface

  • 定義物件結構
  • 需要宣告合併 (擴展第三方型別)
  • 類別要實作的契約
// 推薦使用 type 的情況
type ID = string | number;
type Status = 'pending' | 'active' | 'completed';
type Point = [number, number];
type Callback = (data: string) => void;

// 推薦使用 interface 的情況
interface User {
  name: string;
  age: number;
}

interface Repository {
  findById(id: string): Promise<User>;
  save(user: User): Promise<void>;
}

實用範例

API 型別定義

type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';

type ApiResponse<T> =
  | {
      success: true;
      data: T;
    }
  | {
      success: false;
      error: string;
    };

type User = {
  id: number;
  name: string;
  email: string;
};

type CreateUserRequest = Omit<User, 'id'>;
type UpdateUserRequest = Partial<CreateUserRequest>;

事件處理

type EventType = 'click' | 'hover' | 'focus' | 'blur';

type EventPayload<T extends EventType> = T extends 'click'
  ? { x: number; y: number }
  : T extends 'hover'
    ? { element: HTMLElement }
    : T extends 'focus' | 'blur'
      ? { target: HTMLElement }
      : never;

type EventHandler<T extends EventType> = (payload: EventPayload<T>) => void;

// 使用
let handleClick: EventHandler<'click'> = (payload) => {
  console.log(payload.x, payload.y);
};

狀態管理

type LoadingState = { status: 'loading' };
type SuccessState<T> = { status: 'success'; data: T };
type ErrorState = { status: 'error'; error: string };

type AsyncState<T> = LoadingState | SuccessState<T> | ErrorState;

function handleState<T>(state: AsyncState<T>): void {
  switch (state.status) {
    case 'loading':
      console.log('載入中...');
      break;
    case 'success':
      console.log('成功:', state.data);
      break;
    case 'error':
      console.log('錯誤:', state.error);
      break;
  }
}