TypeScript for React Developer

Generics

Getting to Know Generics

Generics allows you to "parameterize" your type. Let's see an example to understand what it means.

Consider the following forEach function.

ts
const forEach = (array: any[], callBack: (element: any) => void) => {
for (let item of array) {
callBack(item);
}
};
const numbers = [1, 2, 3];
forEach(numbers, item => {
console.log(item.toFixed(2));
console.log(item.padStart()); // runtime error
});

You can of couse create a wrapper of forEach:

ts
const forEach = (array: any[], callBack: (element: any) => void) => {
for (let item of array) {
callBack(item);
}
};
const forEachNumber = (array: number[], callBack: (element: number) => void) =>
forEach(array, callBack);
const numbers = [1, 2, 3];
forEachNumber(numbers, item => {
console.log(item.toFixed(2));
console.log(item.padStart()); // type error
});

But that will be painful when you have to create a wrapper for each data type, when there are zero behavior improvement.

What you want is a way to say, whatever type of the item in the array will be the type of the element passed to callback.

Generics allows us to do so:

ts
const forEach = <Item>(array: Item[], callBack: (element: Item) => void) => {
for (let item of array) {
callBack(item);
}
};
const numbers = [1, 2, 3];
forEach(numbers, item => {
console.log(item.toFixed(2));
console.log(item.padStart()); // type error, yay!
});

Item in the example above is called generics, it allows us to provide a "placeholder" for our type, so we can specify the relationship between multiple variables.

Other Generics Format

Generics are not only used for function. We can used it for other TypeScript syntax too.

ts
class Queue<T> {
private data: T[] = [];
push(item: T) {
this.data.push(item);
}
pop() {
return this.data.shift();
}
}
const queue = new Queue<number>();
queue.push(3);
queue.push('three'); // Error
type Wrapped<ValueType> = {
value: ValueType;
id: string;
};
const numberObject: Wrapped<number> = {
value: 5,
id: 'five',
};
const stringObject: Wrapped<string> = {
value: 'hello',
id: 'h',
};
interface Config<Type> {
type: Type;
required: boolean;
}
interface SpecialConfig extends Config<'special'> {
name: string;
}
const myConfig: SpecialConfig = {
type: 'special',
required: false,
name: 'Special One',
};

Note that for the Wrapped and Config example above, the generics are not to provide some typechecking, but allow us to conveniently create some special type based on our use case.

An example of this is the Promise type that is included in TypeScript.

ts
const getJokeText = (): Promise<string> =>
fetch('https://icanhazdadjoke.com/', {
headers: {
Accept: 'text/plain',
},
}).then(res => res.text());
getJokeText().then(joke => console.log(joke));
type Joke = {
id: string;
joke: string;
status: number;
};
const getJoke = (): Promise<Joke> =>
fetch('https://icanhazdadjoke.com/', {
headers: {
Accept: 'application/json',
},
}).then(res => res.json());
getJoke().then(joke => console.log(joke));
Comments

There is no comment on this post yet.

Issue on this page? Report here