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 Alias | Interface |
|---|---|---|
| 物件型別 | ✓ | ✓ |
| 聯合型別 | ✓ | ✗ |
| 交集型別 | ✓ (用 &) | 用 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;
}
}