【TypeScript】Understanding Generics In 5 Minutes

TiShow
5 min readFeb 28, 2023

What are Generics?

First of all, languages like C# and Java also have generics, so it’s not limited to TypeScript.

Generics which abstract type arguments use are used to implement classes, functions, and interfaces that types are not determined until they are actually used.

Advantages of Generics

Programming languages such as Java, C #, TypeScript, etc. have the characteristic of “statically-typed language”.

A statically-typed language is one that verifies the type safety of values and objects at compile time.

As an antonym, there is a “dynamically-typed language” such as JavaScript, Ruby, Python, etc.

A statically-typed language has the advantage of being “easy to detect errors in advance” instead of imposing the constraint of “strictly describing the type”.

On the other hand, Dynamically typed languages have the advantage of reducing the amount of code you write. Because you don’t have to write the type, but they have the disadvantage that you can’t see the type error unless you try to execute it.

If you try to write all the processing with the same code with a statically-typed language, you have to prepare as many processes as the number of types. However, if we ignore types, the advantages of statically typed languages will be lost.

For example, in TypeScript, if you declare “any” for the type, the type constraint will disappear, but you will not be able to detect errors in advance.

That’s why Generics is needed.

Generics make it possible to program as a statically-typed language while keep both “type safety” and “common code”.

Since it is no longer necessary to write code for each type, the overall amount of code written is reduced, and the overall program is very easy to understand.

Defining functions with generics

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

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

function boolArg(arg: boolean) {
return arg;
}

const num = numberArg(1);
const str = stringArg("me");
const bool = boolArg(true);

In the example above, we need to define multiple functions for the types we want to work with.

These functions are

function anyArg(arg: any): any {
return arg;
}

const num = genericsArg(1);
const str = genericsArg("me");
const bool = genericsArg(true);

If you define it as above, no error will occur (unless you prevent any with tsconfig etc.).
However, the assigned variables num, str, and bool are all of “any” type. Therefore, the benefits of using TypeScript cannot be fully utilized.

function genericsArg<T>(arg: T): T {
return arg;
}

const num = genericsArg<number>(1);
const str = genericsArg<string>("me");
const bool = genericsArg<boolean>(true);

The <T> in genericsArg<T> is the definition of the type variable name.

Conventionally, T is often used, but

function genericsArg<TiShow>(arg: TiShow): TiShow {
return arg;
}

Even if you declare it as TiShow(this is my name haha), there is no problem as an operation.

The last: T declares that the return value will be of type T. Not only T is defined here, but also string, number, and various other objects.

Let’s remember that in TypeScript, “definition of type variable name”, “specification of argument type”, and “specification of return value” are declared in order.

Specifying multiple type arguments

A generic can have more than one type argument.

function test<T, U, P>(arg1:T, arg2:U, arg3:P) {
console.log(arg1);
console.log(arg2);
console.log(arg3);
}

test("wow", 2, true)

You can freely name instead of “U, P”.

By convention, capital letters such as T and U are often used.

If there are no special instructions, follow conventions as much as possible.

Defining classes with generics

Generics can also be used in classes.

class GenericsItem<T> {
private readonly item: T;

constructor(item: T) {
this.item = item;
}

get getItem(): T {
return this.item;
}

public addItemList(list: Array<T>) {
list.push(this.item);
return list;
}
}

When instantiating a class, the defining side can determine the type.

When you want to handle GenericsItem<T> class as string type

const strItem = new GenericsItem<string>(“string Item”);
console.log(strItem.getItem);

const strList = [“string1”, “string2”, “string3”]
console.log(strItem.addItemList(strList));

=> “string Item”
=> [“string1”, “string2”, “string3”, “stringItem”]

If you want to handle GenericsItem<T> class as number type

const numItem = new GenericsItem<number>(5);
console.log(numItem.getItem);

const numList = [1, 2, 3]
console.log(numItem.addItemList(numList));

5
[1, 2, 3, 5]

While having the type checked, it seems that the implementation has a high degree of freedom.

If you try to write code like the following, you will get an error.

const numItem = new GenericsItem<number>(5);
console.log(numItem.getItem);

const strList = [“1”, “2”, “3”]
console.log(numItem.addItemList(strList)); // Error because we can only receive a list of num[]

Constrain type arguments

Arguments defined in T so far can accept a variety of types. But it doesn’t have type information yet at the stage of writing.

So you can’t call properties or methods belonging to the type.

I am trying to get the age from the argument arg in the example below, but this will result in an error.

function getAge<T>(arg: T): number {
return arg.age; // gives error Property ‘age’ does not exist on type ‘T’
}

Since the type of arg is not determined at the stage of definition, the compiler will issue a warning that “I don’t know such a property”.

In this case, you can call the age property by defining an interface as follows and declaring it with the extends word in T.

interface humanData {
name: string;
age: number;
}

function getAge<T extends humanData>(arg: T): number {
return arg.age;


console.log(getAge({ name: “Medium”, age: 18 }));

=> 18

Conclusion

To sum up what generics can do, it is a function that makes it possible to perform strict type checking while reducing the effort on the user side (preparing multiple types, etc.).

The return type can vary depending on the types of the arguments, or the type of the other arguments can vary depending on the first argument passed.

Generics seems difficult to get into if you don’t know anything about it, but once you start writing code, you’ll realize that it’s a useful feature!

I didn’t really know how to use it until I wrote this article.

TypeScript Playground

My understanding for Generics has deepened considerably by writing while actually verifying the operation.

--

--

TiShow

80% is the creation, the rest is depression. Developer and Data scientist. Looking for Physics Ph.D Twitter: @_t_i_show