首頁  >  文章  >  web前端  >  掌握 TypeScript:了解擴充的力量

掌握 TypeScript:了解擴充的力量

Barbara Streisand
Barbara Streisand原創
2024-09-22 06:20:08710瀏覽

Mastering TypeScript: Understanding the Power of extends

TypeScript 中的 extends 關鍵字就像一把瑞士軍刀。它可用於多種上下文,包括繼承、泛型和條件類型。了解如何有效地使用擴充功能可以產生更健壯、可重複使用且類型安全的程式碼。

使用擴充功能進行繼承

extends 的主要用途之一是繼承,允許您建立基於現有介面或類別的新介面或類別。

interface User {
  firstName: string;
  lastName: string;
  email: string;
}

interface StaffUser extends User {
  roles: string[];
  department: string;
}

const regularUser: User = {
  firstName: "John",
  lastName: "Doe",
  email: "john@example.com"
};

const staffMember: StaffUser = {
  firstName: "Jane",
  lastName: "Smith",
  email: "jane@company.com",
  roles: ["Manager", "Developer"],
  department: "Engineering"
};

在此範例中,StaffUser 擴充了 User,繼承了其所有屬性並新增了新屬性。這使我們能夠基於更通用的類型來建立更具體的類型。

類別繼承

extends 關鍵字也用於類別繼承:

class Animal {
  constructor(public name: string) {}

  makeSound(): void {
    console.log("Some generic animal sound");
  }
}

class Dog extends Animal {
  constructor(name: string, public breed: string) {
    super(name);
  }

  makeSound(): void {
    console.log("Woof! Woof!");
  }

  fetch(): void {
    console.log(`${this.name} is fetching the ball!`);
  }
}

const myDog = new Dog("Buddy", "Golden Retriever");
myDog.makeSound(); // Output: Woof! Woof!
myDog.fetch(); // Output: Buddy is fetching the ball!

這裡,Dog 擴展了 Animal,繼承了它的屬性和方法,同時也加入了它自己的屬性和方法。

泛型中的類型約束

extends 關鍵字在使用泛型時至關重要,它允許我們限制可與泛型函數或類別一起使用的類型。

interface Printable {
  print(): void;
}

function printObject<T extends Printable>(obj: T) {
  obj.print();
}

class Book implements Printable {
  print() {
    console.log("Printing a book.");
  }
}

class Magazine implements Printable {
  print() {
    console.log("Printing a magazine.");
  }
}

const myBook = new Book();
const myMagazine = new Magazine();

printObject(myBook);      // Output: Printing a book.
printObject(myMagazine);  // Output: Printing a magazine.
// printObject(42);       // Error, number doesn't have a 'print' method
  1. 介面Printable:這裡,我們定義了一個名為Printable的介面。該介面聲明了任何實現它的類別都必須遵守的契約。契約規定任何實作 Printable 的類別必須提供一個名為 print 的方法,不帶參數並傳回 void
  2. function printObject(obj: T):這是一個名為 printObject 的通用函數。它採用名為 obj 的單一參數,該參數為 T 型別。類型參數 T 被限制為擴展(實作)Printable 介面的類型,可以用作此函數的參數。
  3. Book 類別實作 Printable,Magazine 類別實作 Printable:這裡,我們定義兩個類別,Book 和 Magazine,它們都實作 Printable 介面。這意味著這些類別必須根據 Printable 介面的約定提供列印方法。
  4. const myBook = new Book(); const myMagazine = new Magazine();:我們建立 Book 和 Magazine 類別的實例。
  5. 列印物件(myBook); and printObject(myMagazine);:我們用 Book 和 Magazine 的實例呼叫 printObject 函數。由於 Book 和 Magazine 類別都實作了 Printable 接口,因此它們滿足 T extends Printable 類型參數的約束。在函數內部,呼叫對應類別的 print 方法,從而產生預期的輸出。
  6. // printObject(42);:如果我們嘗試使用未實作 Printable 介面的型別(例如數字 42)呼叫 printObject,TypeScript 將會引發錯誤。這是因為不符合類型約束,因為 number 沒有 Printable 介面所要求的列印方法。

總之,函數 printObject(obj: T) 上下文中的 extends 關鍵字用於確保用作參數的類型 T 遵守 Printable 介面定義的約定。這確保只有具有 print 方法的類型才能與 printObject 函數一起使用,從而強制執行函數使用的特定行為和契約。

條件類型

T extends U ? X : Y
  • T 是正在檢查的類型
  • U 是 T 正在檢查的條件類型。
  • 如果 T 擴充(可指派給)U,則 X 是條件類型求值的型別
  • Y 是 T 不擴充 U 時條件類型求值的類型
type ExtractNumber<T> = T extends number ? T : never;

type NumberOrNever = ExtractNumber<number>; // number
type StringOrNever = ExtractNumber<string>; // never

這裡,ExtractNumber 類型採用型別參數 T。條件類型檢查 T 是否擴展了數字類型。如果是,則類型解析為 T(這是數字類型)。如果不存在,則類型解析為 never。

具有聯合類型的 extends 關鍵字

現在,讓我們考慮表達式 A |乙| C 擴充了 A。乍看之下這似乎違反直覺,但在 TypeScript 中,這個條件實際上是錯誤的。原因如下:

  1. 在TypeScript 中,當您在左側使用帶有聯合類型的extends 時,相當於問:「這個聯合中的每個可能的類型都可以分配給右側的類型嗎?」
  2. 換句話說,A |乙| C 擴展 A 正在詢問:「可以將 A 分配給 A,並且可以將 B 分配給 A,並且可以將 C 分配給 A?」
  3. 雖然 A 當然可以分配給 A,但 B 和 C 可能無法分配給 A(除非它們是 A 的子類型),因此總體結果為 false。
type Fruit = "apple" | "banana" | "cherry";
type CitrusFruit = "lemon" | "orange";

type IsCitrus<T> = T extends CitrusFruit ? true : false;

type Test1 = IsCitrus<"lemon">; // true
type Test2 = IsCitrus<"apple">; // false
type Test3 = IsCitrus<Fruit>; // false

在此範例中,IsCitrus;是假的,因為水果聯盟中並非所有水果都是柑橘類水果。

最佳實踐和技巧

  • 使用擴充來建立有意義的關係:僅當類型之間存在明確的「is-a」關係時才使用繼承。
  • 優先選擇組合而不是繼承:在許多情況下,組合(使用介面和類型交集)比類別繼承更靈活。
  • 謹慎使用深層繼承鏈:深層繼承會使程式碼更難理解和維護。
  • 利用條件類型實作靈活的 API:使用具有擴充的條件類型來建立根據輸入類型進行調整的 API。
  • 在泛型中使用擴充功能來建立可重複使用、類型安全的函數:這允許您編寫適用於各種類型的函數,同時仍保持類型安全

以上是掌握 TypeScript:了解擴充的力量的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn