Tutorial

Module Augmentation in TypeScript

Published on September 5, 2019
Default avatar

By Alfred M. Adjei

Module Augmentation in TypeScript

While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.

Before getting into module augmentation, let’s look at some TypeScript merging principles which will become useful as we progress.

In this post we talked about merging interfaces with interfaces. Additionally, we can merge interfaces with classes too. Let’s look at an example.

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"}

In our example above, we can see that, the food variable contains both bacon and cheese even though only cheese was declared in the Food class. This is because, the interface was merged with the class.

But what if our interface contains a method, for example, bake

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

Though, the bake method will be shown on the food variable with the help of intelliSense, because the class Food and the interface Food will be merged, calling the bake method will result in an error because interfaces contain only declaration but not implementations. To solve this, we can add the implementation of bake to the Food prototype.

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

Now calling the bake method will work.

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

Enter Module Augmentation

Module augmentation helps us to extend functionalities to third party libraries we may not have access to or classes in other files.

Say we have a Pet class with a name property and feed method.

pet.ts
export class Pet {
  name: string;

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

We then decide to import this class into our index.ts file but instead of using only the methods and properties in the Pet class, we want to add more functionalities. We can do that using module augmentation.

First, we import our Pet class into our index.ts file.

index.ts
import { Pet } from "./pet";

./pet is a module. In order to extend it, we have a declare a module using the same name and in that module, we will declare an interface with the same name as the class we are trying to extend. In the interface, we will include the properties and methods we want to add to the extended class.

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

TypeScript will merge both the Pet class and the Pet interface because they can be found in the same ./pet module.

But that’s not all. Remember I explained that, interfaces don’t contain implementation for methods but only their declarations. For that reason, we will add the implementation of the walk method to the prototype of Pet.

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

Now we can call both the methods and properties found in the Pet class and the newly declared Pet interface.

index.ts
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

Now you may be wondering, instead of declaring an interface then adding the implementation of walk method to the Pet prototype, why didn’t we just declare a class with the same name so that when the class is initialized, we will have methods from both classes?

The answer is, TypeScript doesn’t allow merging between classes so we can’t create two or more classes with the same name. If you want to merge classes, there is a workaround using TypeScript mixins discussed about in this post or you can use a library I created just for that.

That’s it. Hope this was useful. 😎👌

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


About the authors
Default avatar
Alfred M. Adjei

author

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
1 Comments


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Dear Alfred M. Adjei, Thank you for the great article!

But I have a question about node_modules class.

I want to augment class from node_modules and write like as your code. But tsc say: TS2339: Property ‘age’ does not exist on type ‘TransactionSettings’. Can you help me to augment external class ?

import { Ydb } from ‘ydb-sdk’;

declare module ‘ydb-sdk’ { // eslint-disable-next-line @typescript-eslint/no-namespace namespace Table { interface TransactionSettings { age: number; walk(location: string): void; } } }

// @ts-ignore Ydb.Table.TransactionSettings.prototype.walk = (location: string) => Likes to walk in the ${location};

const a = new Ydb.Table.TransactionSettings({ serializableReadWrite: {} }); console.log(a);

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel