How to make Generic Interfaces that change type according to generic parameter type in TypeScript?

January 11, 2022 - 4 min read

To make Generic Interfaces that change the type according to the generic parameter, we can use the angled brackets symbol (<>) after the interface name and inside the brackets symbol, we can write any name for the generic parameter. At last, inside the type declaration body, we can use the generic parameter name we defined wherever we need to use it as a general or generic type.

TL;DR

// A generic interface
// with generic parameter called `Type`
interface Books<Type> {
  contents: Type;
}

// creating an object using the
// generic interface `Books` and passing `string`
// as the generic parameter type
const book: Books<string> = {
  contents: "Hello World", // <-- the `contents` property now only accepts value of `string` type
};

// creating an object using the
// generic interface `Books` and passing `number`
// as the generic parameter type
const book2: Books<number> = {
  contents: 12345, // <-- the `contents` property now only accepts value of `number` type
};

To understand it a little more clearly, let's consider an interface called Books like this,

// Books interface
interface Books {
  // type declaration here
}

Now let's declare a propety called contents and give it a type of string like this,

// Books interface
// where `contents` property
// is of type `string`
interface Books {
  contents: string;
}

Now we have an interface called Books where the contents should be of string type only. This is fine.

But what if we need the contents property to be of type number for another kind of Book.

So you may think of writing one more interface called Books2 where the contents property is of number type like this,

// Books interface
// where `contents` property
// is of type `string`
interface Books {
  contents: string;
}

// Books2 interface
// where `contents` property
// is of type `number`
interface Books2 {
  contents: string;
}

Now looking at the above 2 interfaces, I believe you got the problem. Even though we have similar kinds of interfaces, the only thing that got changed is the contents property's type.

This is not a good and scalable way of achieving the solution.

This is where the concept of Generic Interfaces comes into play.

A Generic interface is written by first writing the generic parameter name in between the angled brackets (<>) after the interface name and then using the generic type wherever we need to use it inside the type declaration body of the interface.

It can be done like this,

// A generic interface
// with generic parameter called `Type`
interface Books<Type> {
  contents: Type;
}

Now when using the Books interface as the type for any objects we can pass the type we need to use at that time by passing it inside the angled brackets symbol (<>) like this,

// A generic interface
// with generic parameter called `Type`
interface Books<Type> {
  contents: Type;
}

// creating an object using the
// generic interface `Books` and passing `string`
// as the generic parameter type
const book: Books<string> = {
  contents: "Hello World", // <-- the `contents` property now only accepts value of `string` type
};

// creating an object using the
// generic interface `Books` and passing `number`
// as the generic parameter type
const book2: Books<number> = {
  contents: 12345, // <-- the `contents` property now only accepts value of `number` type
};

As you can see from the above code we have made 2 objects called book and book2 and used the same generic interface called Books and passed different types as the generic type and was able to reuse the interface.

We have successfully made Generic interfaces. Yay 🥳!

See the above code live in codesandbox.

Feel free to share if you found this useful 😃.