TypeScript 泛型基礎與工具型別
如果有個情境:
不預先指定具體的型別,在使用的時候動態決定型別的一種特性。
直覺想到 any,但這樣的寫法並不能限制回傳值的型別,不具備保護功能。
function createArray(arg: any): any {
return arg;
}
createArray(true); // ok
createArray(''); // ok
若是搭配泛型,能進一步約束回傳值型別:
function createArray<T>(arg: T): T {
return arg + 1; // error
}
因為你傳入什麼,就要回傳什麼。
泛型跟 any 差別在於 泛型可更精確的定義回傳值的型別
可定義多個泛型
當函式有多個參數時,怎麼定義多個泛型?
function multiple<T, A>(arg1: T, arg2: A): T {
return arg1;
}
泛型約束
所謂的「泛型約束」指的就是根據需求,限縮泛型可傳入值的型別,例如我們今天希望傳入的值必須要有 length 這個屬性才能。
我們可以透過 extends 這個方法來實現泛型約束,以下為例:
約束傳入的值必須要包含 length 這個屬性,這個行為就被稱為 泛型約束(稍微限縮可傳入的類型)。
interface IncludeLength {
length: number;
}
function logMessage<T extends IncludeLength>(arg: T): T {
console.log(arg.length);
return arg;
}
常用工具型別
TypeScript 內建了許多實用的工具型別(Utility Types),它們都是基於泛型實現的,可以幫助我們更方便地操作型別。
Partial<T>
產生一個全新的型別,把參照的所有屬性轉為可選。
interface User {
id: number;
name: string;
email: string;
}
const updateUser = (user: Partial<User>) => {
// 可以只更新部分屬性
console.log(user);
};
updateUser({ name: "Andy" }); // ✅ 正確,只更新 name
updateUser({}); // ✅ 也可以是空物件
Required<T>
與 Partial 相反, 讓所有屬性變成必填。
interface OptionalUser {
id?: number;
name?: string;
email?: string;
}
const createUser = (user: Required<OptionalUser>) => {
// 現在所有屬性都是必填
console.log(user.id, user.name, user.email); // 不會有 undefined 的問題
};
Pick<T, K>
從型別 T 中挑選特定的屬性 K。
type UserProfile = Pick<User, 'name' | 'email'>;
// 等同於:{ name: string; email: string; }
const showProfile = (profile: UserProfile) => {
console.log(profile.name, profile.email);
// profile.id // ❌ 錯誤,id 不存在
};
Omit<T, K>
從型別 T 中排除特定的屬性 K。
type CreateUserData = Omit<User, 'id'>;
// 等同於:{ name: string; email: string; }
const createNewUser = (userData: CreateUserData) => {
// 新增使用者時不需要提供 id(通常由系統生成)
return { id: Date.now(), ...userData };
};
Record<K, T>
建立一個鍵為 K、值為 T 的鍵值對型別。
type UserRoles = Record<string, boolean>;
// 等同於:{ [key: string]: boolean; }
const permissions: UserRoles = {
read: true,
write: false,
delete: true
};
// 或者更具體的鍵
type Status = 'pending' | 'approved' | 'rejected';
type StatusConfig = Record<Status, string>;
const statusMessages: StatusConfig = {
pending: '審核中',
approved: '已通過',
rejected: '已拒絕'
};