博客 / 詳情

返回

什麼是 TypeScript 的 Module Augmentation

在進入模塊擴充之前,讓我們看看一些 TypeScript 合併原則,這些原則將隨着我們的進步而變得有用。

TypeScript 支持創建同名的 class 和 interface:

class Food {
  cheese: string;
}

interface Food {
  bacon: string;
}

const food  = new Food();
food.bacon = "nice bacon";
food.cheese = "sweet cheese";

console.log(food); // {bacon: "nice bacon", cheese: "sweet cheese"}

在我們上面的例子中,我們可以看到,即使在 Food 類中只聲明瞭 cheese,食物變量也包含 bacon 和 cheese。 這是因為,接口與類合併了。

但如果 interface 裏包含的是方法,結果又如何?

class Food {
  cheese: string;
}

interface Food {
  bacon: string;
  bake(item: string);
}

const food  = new Food();

food.bake("cake"); // Error: food.bake is not a function

但是,bake 方法會在intelliSense 的幫助下顯示在food 變量上,因為Food 類和接口Food 將合併,調用bake 方法會導致錯誤,因為接口只包含聲明而不包含實現。 為了解決這個問題,我們可以將bake的實現添加到Food原型中。

Food.prototype.bake = (item) => console.log(item);

之後 bake 方法調用就能夠工作了:

food.bake("cake"); // cake

模塊擴充幫助我們將功能擴展到我們可能無法訪問的第三方庫或其他文件中的類。

假設我們有一個帶有 name 屬性和 feed 方法的 Pet 類。

export class Pet {
  name: string;

  feed(feedType: string) {
    console.log(feedType);
  }
}

然後我們決定將這個類導入到我們的 index.ts 文件中,但不是隻使用 Pet 類中的方法和屬性,我們想要添加更多功能。 我們可以使用模塊擴充來做到這一點。

首先,我們將 Pet 類導入到 index.ts 文件中。

import { Pet } from "./pet";

./pet 是一個模塊。 為了擴展它,我們聲明瞭一個使用相同名稱的模塊,在該模塊中,我們將聲明一個與我們嘗試擴展的類同名的接口。 在接口中,我們將包含要添加到擴展類的屬性和方法。

declare module "./pet" {
  interface Pet {
    age: number;
    walk(location: string);
  }
}

TypeScript 將合併 Pet 類和 Pet 接口,因為它們可以在同一個 ./pet 模塊中找到。

但這還不是全部。 記住我解釋過,接口不包含方法的實現,而只包含它們的聲明。 為此,我們將在 Pet 的原型中添加 walk 方法的實現。

Pet.prototype.walk = (location:string) => `Likes to walk in the ${location}`

現在我們可以調用 Pet 類中的方法和屬性以及新聲明的 Pet 接口。

const pet = new Pet();

pet.name = "Looty";
pet.age = 3;

pet.feed("bacon"); // bacon
console.log(pet.name = "Looty"); // Looty
console.log(pet.age = 3); // 3
console.log(pet.walk("park")); // Likes to walk in the park

現在你可能想知道,與其先聲明一個接口,然後在 Pet 原型中添加 walk 方法的實現,為什麼我們不直接聲明一個同名的類,這樣當類被初始化時,我們將擁有來自兩者的方法 課?

答案是,TypeScript 不允許在類之間合併,所以我們不能創建兩個或多個同名的類。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.