TypeScript,除了是個 JavaScript 的強大超集之外,因為它提升了程式碼的質量和可維護,迅速成為開發者們的心頭好。
不過,很多人可能只停留在基本概念,例如類型、介面和類別的層次上。
借此機會,讓我們深入了解一些高階功能,讓我們的開發效率再度加倍提升。
在這篇文章中,我們會透過實際範例和實務應用案例,帶大家一步步了解這些功能的魅力。
我們將深入探討三個 TypeScript 的高階功能:
泛型允許你創建可以接受任意類型的元件,讓你的程式碼更加靈活且具重複使用性。
它特別適用於函數、類別和介面中。
泛型函數能在保留類型安全的情況下,操作多種類型。以下是一個簡單的例子:
function identity<T>(arg: T): T {
return arg;
}
const numberResult = identity(10); // numberResult 為 number 類型
const stringResult = identity("Hello"); // stringResult 為 string 類型
這個 identity
函數是一個泛型函數,它接受一個類型參數 T
和一個 T
類型的參數,並返回相同類型的值。這樣一來,我們就可以在不同的類型之間調用這個函數而不會喪失類型安全性。
class GenericBox<T> {
private contents: T;
constructor(contents: T) {
this.contents = contents;
}
getContents(): T {
return this.contents;
}
}
const numberBox = new GenericBox(100);
const stringBox = new GenericBox("TypeScript");
在這裡,GenericBox
是一個可以持有任意類型 T
的類別,使得該類別可以針對不同類型重複使用而無需重寫邏輯。
💡 小提醒:泛型的使用千萬不要過度,
尤其在你試圖解決的問題並不需要泛型的時候。
泛型非常適合在需要時靈活應用,但過度複雜的泛型設計往往適得其反。
function printLength<T extends { length: number }>(arg: T): void {
console.log(arg.length);
}
printLength("Hello"); // 有效,string 有 length 屬性
printLength([1, 2, 3]); // 有效,array 有 length 屬性
這個函數限制 T
只接受具有 length
屬性的類型,確保了類型安全性,同時仍保有泛型的靈活性。
裝飾器是一個強大的功能,可以為類別宣告和成員添加註釋和元程式設計語法。在像 Angular 和 NestJS 這樣的框架中被廣泛使用。
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class ExampleClass {
// ...
}
這裡的 sealed
裝飾器會封裝這個類別,防止新屬性被添加到類別中。
function log(target: any, propertyName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Calling ${propertyName} with arguments: ${args}`);
const result = originalMethod.apply(this, args);
console.log(`Result: ${result}`);
return result;
};
}
class Calculator {
@log
add(a: number, b: number) {
return a + b;
}
}
const calc = new Calculator();
calc.add(5, 10);
在這個例子中,log
裝飾器會記錄 add
方法的參數和結果,提供方法調用的透明度。
條件類型可以根據條件執行複雜的類型轉換,用於創建靈活且強大的類型定義。
type IsString<T> = T extends string ? "yes" : "no";
type A = IsString<string>; // "yes"
type B = IsString<number>; // "no"
這裡的 IsString
會檢查 T
是否為 string 類型,若是則返回 "yes",否則返回 "no"。
type ElementType<T> = T extends (infer U)[] ? U : T;
type C = ElementType<number[]>; // number
type D = ElementType<string>; // string
在這個例子中,ElementType
會提取數組中的元素類型,如果不是數組則返回該類型本身。
💡 實務應用:深度唯讀(Deep Readonly)
一個實務的條件類型範例是創建深度唯讀類型,將物件的所有屬性和子屬性設為唯讀:
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};
type User = {
name: string;
address: {
street: string;
city: string;
};
};
const user: DeepReadonly<User> = {
name: "Alice",
address: {
street: "123 Main St",
city: "Wonderland",
},
};
// user.address.street = "456 Side St"; // 這樣的修改會報錯
DeepReadonly
會遞迴地將 User
的所有屬性設為唯讀(Readonly),確保物件的不可變性。
泛型 (Generics)
裝飾器 (Decorators)
條件類型 (Conditional Types)
應用這些特性的好處
在你的專案裡快來試試看這些高階特性吧~✨保證會讓你的開發流程大大升級,變得更有趣喔!😆🚀💡