TypeScript Generics

TypeScript Generics is a tool which provides a way to create reusable components. It creates a component that can work with a variety of data types rather than a single data type. It allows users to consume these components and use their own types. Generics ensures that the program is flexible as well as scalable in the long term.

Generics provides type safety without compromising the performance, or productivity. TypeScript uses generics with the type variable which denotes types. The type of generic functions is just like non-generic functions, with the type parameters listed first, similarly to function declarations.

In generics, we need to write a type parameter between the open (<) and close (>) brackets, which makes it strongly typed collections. Generics use a special kind of type variable <T> that denotes types. The generics collections contain only similar types of objects.

In TypeScript, we can create generic classes, generic functions, generic methods, and generic interfaces. TypeScript Generics is almost similar to C# and Java generics.

Example

The below example helps us to understand the generics clearly.

snippet
function identity<T>(arg: T): T {  
    return arg;  
}  
let output1 = identity<string>("myString");  
let output2 = identity<number>( 100 );
console.log(output1);
console.log(output2);

When we compile the above file, it returns the corresponding JavaScript file as below.

snippet
function identity(arg) {
    return arg;
}
var output1 = identity("myString");
var output2 = identity(100);
console.log(output1);
console.log(output2);
TypeScript Generics

Advantage of Generics

There are mainly three advantages of generics. They are as follows:

  1. Type-safety: We can hold only a single type of objects in generics. It doesn't allow to store other objects.
  2. Typecasting is not required: There is no need to typecast the object.
  3. Compile-Time Checking: It is checked at compile time so the problem will not occur at runtime.

Why need Generics?

We can understand the need for generics by using the following example.

Example #1
snippet
function getItems(items: any[] ) : any[] {
    return new Array().concat(items);
}
let myNumArr = getItems([10, 20, 30]);
let myStrArr = getItems(["Hello", "rookienerd"]);
myNumArr.push(40); // Correct
myNumArr.push("Hello TypeScript"); // Correct
myStrArr.push("Hello SSSIT"); // Correct
myStrArr.push(40); // Correct
console.log(myNumArr); // [10, 20, 30, 40, "Hello TypeScript"]
console.log(myStrArr); // ["Hello", "rookienerd", "Hello SSSIT", 40]
TypeScript Generics

In the above example, the getItems() function accepts an array which is of type any. The getItems() function creates a new array of type any, concatenates items to it and returns this new array. Since we have used any data type, we can pass any type of items to the function. But, this may not be the correct way to add items. We have to add numbers to number array and the strings to the string array, but we do not want to add numbers to the string array or vice-versa.

To solve this, TypeScript introduced generics. In generics, the type variable only accepts the particular type that the user provides at declaration time. It is also preserving the type checking information.

So, we can write the above function in generic function as below.

Example #2
snippet
function getItems<T>(items : T[] ) : T[] {
    return new Array<T>().concat(items);
}
let arrNumber = getItems<number>([10, 20, 30]);
let arrString = getItems<string>(["Hello", "rookienerd"]);
arrNumber.push(40); // Correct
arrNumber.push("Hi! rookienerd"); // Compilation Error
arrString.push("Hello TypeScript"); // Correct
arrString.push(50); // Compilation Error
console.log(arrNumber);
console.log(arrString);
TypeScript Generics

In the above example, the type variable T specifies the function in the angle brackets getItems<T>. This variable also specifies the type of the arguments and the return value. It ensures that data type specified at the time of a function call will also be the data type of the arguments and the return value.

The generic function getItems() accepts the numbers array and the strings array. When we call the function getItems<number>([10, 20, 30]), then it will replace T with the number. So, the type of the arguments and the return value will be number array. Similarly, for function getItems<string>(["Hello", "rookienerd"]), the arguments type and the return value will be string array. Now, if we try to add a string in arrNumber or a number in arrString array, the compiler will show an error. Thus, it preserves the type checking advantage.

In TypeScript, we can also call a generic function without specifying the type variable. The TypeScript compiler will set the value of T on the function based on the data type of argument values.

Multi-type variables

In TypeScript Generics, we can define multi-type variables with a different name. We can understand it with the following example.

Example
snippet
function displayDataType<T, U>(id:T, name:U): void { 
  console.log("DataType of Id: "+typeof(id) + "\nDataType of Name: "+ typeof(name));  
}
displayDataType<number, string>(101, "Abhishek");
TypeScript Generics

Generic with non-generic Type

We can also use generic types with other non-generic types.

Example
snippet
function displayDataType<T>(id:T, name:string): void { 
  console.log("DataType of Id: "+typeof(id) + "\nDataType of Name: "+ typeof(name));  
}
displayDataType<number>(1, "Abhishek");
TypeScript Generics

Generics Classes

TypeScript also supports generic classes. The generic type parameter is specified in angle brackets (<>) following the name of the class. A generic class can have generic fields or methods.

Example
snippet
class StudentInfo<T,U>
{ 
    private Id: T;
    private Name: U;
    setValue(id: T, name: U): void { 
        this.Id = id;
        this.Name = name;
    }
    display():void { 
        console.log(`Id = ${this.Id}, Name = ${this.Name}`);
    }
}
let st = new StudentInfo<number, string>();
st.setValue(101, "Virat");
st.display();
let std = new StudentInfo<string, string>();
std.setValue("201", "Rohit");
std.display();
TypeScript Generics

Generics Interface

The generic type can also be used with the interface. We can understand the generic interface with the following example.

Example
snippet
interface People {
    name: string
    age: number
}
interface Celebrity extends People {
    profession: string
}
function printName<T extends Celebrity>(theInput: T): void {
    console.log(`Name: ${theInput.name} \nAge: ${theInput.age} \nProfession: ${theInput.profession}`);
}
let player: Celebrity = {
    name: 'Rohit Sharma', age: 30, profession: 'Cricket Player'
}
printName(player);
TypeScript Generics

Generics Interface as Function Type

We can also use generics interface as function types. The following example can understand it.

Example
snippet
interface StudentInfo<T, U>
{
    (id: T, value: U): void;
};
function studentData(id: number, value:string):void { 
    console.log('Id = '+ id + ', \nName = ' + value)
}
let std: StudentInfo<number, string> = studentData;
std(11, "Rohit Sharma");
TypeScript Generics

Generic Constraints

As we know, the TypeScript Generics Types allows working with any and all data type. However, we can restrict it to certain types by using constraints. In the following example, we will create an interface that has a single .length property. We will use this interface, and the "extends" keyword to denote our constraint.

Example
snippet
interface Lengthwise {
    length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log("Length: " +arg.length);  // It has a .length property, so no more error found
    return arg;
}
loggingIdentity({length: 10, value: 9});
loggingIdentity(3);  // Compilation Error, number doesn't have a .length property
Output
Length: 10 Length: undefined

Generic Constraints with class

A more advanced example of Generic constraints relationships between the constructor function and the instance side of class types is given below.

Example
snippet
class Student {
    Id: number;
    Name: string;

    constructor(id:number,  name:string) { 
        this.Id = id;
        this.Name = name;
    }
}
function display<T extends Student>(per: T): void {
    console.log(`${ st.Id} ${st.Name}` );
}
var st = new Student(101, "\nVirat Kohli");
display(st);
TypeScript Generics
Related Tutorial
Follow Us
https://www.facebook.com/Rookie-Nerd-638990322793530 https://twitter.com/RookieNerdTutor https://plus.google.com/b/117136517396468545840 #
Contents +