Get job ready skills with Codenga     |       Career Paths 40% OFF     |        Limited time only

2d 01h
close
Cart icon
User menu icon
User icon
Lightbulb icon
How it works?
FAQ icon
FAQ
Contact icon
Contact
Terms of service icon
Terms of service
Privacy policy icon
Privacy Policy
Zdjęcie główne artykułu.

TypeScript - Generics

Generics are an important part of the TypeScript language. They can be described as flexible components that come with a type safety. Generics include functions, classes and interfaces. They come very handy in numerous situations so it’s really important to learn how they work.

In this article we will introduce you to the concept of TypeScript Generics. You will learn how generic functions, classes and interfaces work.

Generic functions

Imagine, you need to create a whole bunch of functions that will accept a single argument and return it. We need a function for every kind of data - number, string, boolean etc.

That’s a very tedious task. And it will require a lot of code duplication. We basically need to create a separate function for every kind of data. Here is an example implementation:

					
function getValueAsnumber(arg: number) {
	return arg;
}

function getValueAsString(arg: string) {
	return arg;
}

//...
					
				

Yep! That will be a chore to create all those functions. Surely, there must be a better way.

The solution is to use a generic function. Such a function is very flexible - it can accept all kinds of input parameters. Here is the code of our basic, generic function:

					
function getValue<Type>(arg: Type): Type {
	return arg;
}
					
				

Note the <Type> element. It’s a placeholder for data. It can accept all types of data. You can name this element as you wish: Type, T etc. It’s a question of the naming convention you are using in your code.

How do we call this function? Take a look:

					
let valueAsString = getValue<string>("Tom");
					
				

While calling the function we substitute a <Type> with a concrete data type. In our case it's a string. You can call the function with a different data type:

					
let valueAsnumber = getValue <number> (1)
					
				

Now we are calling our generic function using a number as a data type. See it?

It’s a very flexible function that can work with all kinds of data types. And we still got type safety in use. We indicate the correct data type while calling the function. TypeScript will be responsible for providing us with a type safety for this call.

In theory you could write a function that will accept any as data type. But that will result in bypassing all type safety checks for this function. That’s why a generic function is a much better solution. It’s a great way to create flexible, reusable functions that enjoy data safety checks.

Generic classes

Generic classes work on a similar principle. You can create a flexible, reusable class that can work with all kinds of data. Here is an example of generic class:

					
class Monitor<K, V> { 
	private key: K;
	private value: V;

	constructor(key: K, value: V) {
		this.key = key;
		this.value = value;
	}

	public display() {
		console.log("key: " + this.key + ", " +  "value: " + this.value);
	}
}

let hd = new Monitor<number, string>(720, "HD");
hd.display();
					
				

Take a look at the class fields: private key: K and private value: V. They have pretty generic names. And they accept all data types. Now let’s try to create an instance of this class:

					
let hd = new Monitor<number, string>(720, "HD");
					
				

When building the object of the class we substitute the K with a value of type number (720). And we substitute the V with a value of type string (“HD”).

You may use different combo of parameters:

					
let small = new Monitor<string, string>("phone", "360");
					
				

Now we use string values for both K and V. See how flexible it is? You can use many different data types and still enjoy the safety checks.

Generic interfaces

We got generic functions and classes covered. The next step is to take a look at generic interfaces. Here is a simple example:

					
interface Operation<A, B, C> {
	value: C;
	show(firstParam: A, secondParam: B): void;
}
					
				

You probably already know how it works. When implementing this interface you need to substitute A, B and C with concrete data types. So let’s try to build a simple class that implements this interface:

					
interface Operation {
	value: C;
	show(firstParam: A, secondParam: B): void;
};
 
class OperationImpl implements Operation {
	public value : number = 7;
 
	public show(firstParam: string, secondParam: number): void {
		console.log("firstParam  = " + firstParam);
		console.log("secondParam = " + secondParam);
	}
}
					
				

A class named OperationImpl implements our Operation interface. We used string, number and number as data types. It goes without saying that you can use totally different data types while implementing this interface. That’s a really flexible mechanism!

As you can see, there is nothing overly complicated about TypeScript Generics. They are a great way to build flexible and reusable components in your code.