iT邦幫忙

2026 iThome 鐵人賽

DAY 17
0

接續昨天泛型的基礎介紹,今天來講講泛型約束。泛型可以讓我們在定義函式、介面或其他結構時,先使用型別參數代表尚未確定的型別,等到實際使用時再決定具體型別。泛型在使用上非常有彈性,但有些情況下,我們會希望泛型不能接受所有型別,而是必須符合某些條件,這時就可以使用「泛型約束」。


泛型約束:extends

extends在繼承中有子類別繼承父類別的意涵,但在泛型中,extends則是用來設定型別約束,要求泛型型別必須符合指定的條件才能使用。

//未使用泛型約束的情況:
function getLength<T>(a: T): number {
	return a.length;
} //不合法,TypeScript(TS)會報錯

getLength("hello");
//因為T可以是任何型別,不一定具有length屬性

//使用泛型約束的情況:
function getLength<T extends {length: number}>(a: T): number {
	return a.length;
}

getLength("hello"); //string有length屬性,合法
getLength([1, 2, 4]); //陣列有length屬性,也合法
getLength(999); //number沒有length屬性,這樣TS會報錯喔

泛型約束:介面

我們也能透過介面使用泛型約束:

//藉由介面設定員工的資料類型
interface employee {
	name: string;
	age: number;
}
//透過泛型約束,可以限制函式接收的資料類型
function employeeInfo<T extends employee>(e: T) {
	console.log(e.name);
	console.log(e.age);
}

let e1 = {
	name: "Judy",
	age: 19
};
employeeInfo(e);

//錯誤示範:
//當我們限制了函式屬性的型別,我們就得要求T至少要符合employee的結構
let e2 = {
	name: "Judy",
	age: "19" //介面定義的age是number,改成字串會跳出型別錯誤的提示
};

//額外補充:
//除了規定的屬性外,也能額外補充其他介面沒有的屬性
let e3 = {
	name: "Judy",
	age: 19,
	gender: "woman"
};

keyof

進階的泛型約束能使用keyofkeyof會將物件型別的屬性名稱轉成聯合型別,接著來看一下範例,比較好說明:

function getProperty<T, K extends keyof T>(obj: T, key: K) {
  return obj[key];
} //K必須是T的其中一個屬性名稱。

//建立一個實際物件student
const student = {
  name: "Steve",
  major: "化學系",
  age: 19
}; 
//當我們把student傳入函式後,T會對應到student的型別,而keyof T就會得到 
//"name" | "major" | "age"`。因此K只能從這三個屬性名稱中選擇。

let studentName = getProperty(student, "name"); //合法
let studentMajor = getProperty(student, "major"); //合法

//錯誤示範:
let myGPA = getProperty(student, "gpa"); 
//前面說到參數K只能是 "name" | "major" | "age"其一,當中沒有gpa,TS偵測到後會報錯
//使用keyof的好處是能幫我們避免存取不存在的屬性

小結

今天補充了泛型在條件約束下的用法,學習有效的運用泛型約束。泛型讓型別具有彈性,而泛型約束則是在「彈性」與「型別安全」之間取得平衡。今天的內容就到這邊,大家明天見啦!


上一篇
[Day-16] Generics泛型
下一篇
[Day-18]Utility Types-1
系列文
從零開始學習TypeScript:菜鳥的入門介紹與概覽22
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言