[TypeScript] Understanding <Generics>

·

3 min read

[TypeScript] Understanding <Generics>

Introduction

In this article, I will be talking about the generics in TypeScript, which is an important concept you need to know. If you know programming languages like Java, C# and GO, you might have heard of it.

Generics allow creating 'type variables' which can be used to create classes, functions & type aliases

Generics are not much different from a parameter. You will be able to see the similarity as we go through the example code.


Why Do We Use Generics?

We use generics to avoid repeating blocks of code. Let's say we need to create a function that requires user input with many different types.

function log(userInput: string) {
    console.log(userInput);    
}

function log(userInput: number) {
    console.log(userInput);    
}

function log(userInput: boolean) {
    console.log(userInput);    
}
// and so on..

OR:

function log(userInput: string | number | boolean | Array<string> | Array<number>) {
    console.log(userInput);    
}
// Code gets messier

OR:

function log(userInput: any) {
    console.log(userInput);
}
// might cause an error

If you are using any, your code more likely causes an error. We are using TypeScript to make JS code more straightforward and less subject to an error.

Generics can solve the problem:

function log<T>(userInput: T): T {
    console.log(userInput);
}
// T is similar to a parameter
log<string>("String type");
log<number>(1004);
log<boolean>(true);

Now the code is much simpler and straightforward. The T (stands for Type) doesn't have to be T, but using T is conventional, which is what everyone does.


Understanding Generics

Let's look at this example again:

function log<T>(userInput: T): T {
    console.log(userInput);
}
// T is similar to a parameter
log<string>("String type");

When you do log<string>, TypeScript reads it in this way:

function log<string>(userInput: string): string {
    //.. 
}

When you use generics, the type is determined when the function is called.


Generics in Interface & Class

Generics in Interface:

interface Student<T> {
    ID: 10245845;
    name: T;
    school: T;
    facalty: T;  
}

let stu: Student<string>;

Generics in Class:

class Movie<T> {
    title: T;
    runningTime: number;
    constructor(title: T, runningTime: number) {
        this.title = title;
        this.runningTime = runningTime;
    }        
}

let movie = new Movie<string>();

Similarly, in class, generics are defined when you create a new instance, just like you gave a type when you call the function.

Note that generic classes are only generic over their instance side rather than their static side, so when working with classes, static members can not use the class’s type parameter.


Length Property

Let's have a look at this code:

function logLength<T>(param: T): void {
    console.log(param.length); // Error!    
}

This code causes an error. TypeScript does not know which type param will be. If param is a number, param.length is an error. Therefore, only string, object and array are valid options.

There are two ways to solve this problem

Solution 1: Using an array

function logLength<T>(param: T[]): void {
    console.log(param.length); 
}

logLength<number>([1, 2, 3]) // 3

Solution 2: Using a pre-defined type

interface Length {
    length: number;        
}

function logLength<T extends Length>(param: T): void {
    console.log(param.length); 
}

Reference

https://www.w3schools.com/typescript/typescript_basic_generics.php

Did you find this article valuable?

Support Lim Woojae by becoming a sponsor. Any amount is appreciated!