Demystifying Generics in TypeScript
Unveiling the Magic of TypeScript Generics for Clearer, More Readable, and Error-Free JavaScript Projects
Why it is necessary to know about generics?
In Typescript generics are mostly everywhere. You’ll be seeing generics everywhere. From Arrays to useEffect. Almost every library utilises generics.
While you may not currently have immediate plans to create generics, it’s important to recognise that you’ll still engage with them extensively in your work
Generics make types (type interface etc.
) re-usable and flexible for variable types (data types).
What a generic is?
Explain to a 10 Year Old:
Generics in TypeScript are like magic potions for your computer code. Imagine you have a special box, and you want to put different types of toys inside — some are cars, some are dolls.
Generics help you tell the box what kind of toys it will hold without knowing exactly what types they are.
It’s like a super-smart box that can work with any kind of toy you give it, making your playtime much more fun and organized!
Explain to a working professional:
In the realm of TypeScript, generics serve as a robust tool that enables the creation of flexible and reusable code components.
Think of them as placeholders or variables that can represent various data types within a function, class, or interface.
This allows for the construction of functions and structures that adapt to different data types without sacrificing type safety. In essence, generics empower developers to write more adaptable and scalable code, fostering code efficiency and maintainability in complex software projects.
Let’s understand this by an example:
below is a function add
, which simply perform addition on two numbers.
Now using such function in our application has some downside:
add
function has only one job. to add2+2
and return the sum.- it is limited to only add
2+2
, what if we want to add another number.
Generally we create functions which work with different set of arguments so that we can leverage them in our code and we don’t have to write same code again and again.
The right implementation of add
function could be like this:
Now let’s se an example in TypeScript. Can you spot the limitation?
Limitation: add
function can only accept number
type and can only return number
type.
What If I have to concatenate two strings
? Then I’ve to add another function with same logic. Now this will be duplication code.
By using generics we can solve this problem, let’s see how can we write a reusable function using generics:
In add
function we are taking two things from user:
- what will be the arguments
- what will be type of arguments, so that we can return that type and perform operation on desired type
And this is called GENERICS in TypeScript. Generics simply make types(interfaces, types, function, classes) reusable by making them work with variable types.
They make typescript to accept type arguments from the user.
SYNTAX:
<>
(angle brackets) : We pass and receive data types using these brackets.type Person<T> = T;
- Arrays are special case, where we can use both
<DataType>
andDataType[]
syntax.const arr:string[] = [""];
orconst arr:<string>[] = [""]
Knowing the syntax of generics helps us to understand things in a better way.
We can categorise the learning into two ways:
1. Creation — How we create a function which accepts generic type
Example 1:
// Generic function to reverse an array
function reverseArray<T>(array: T[]): T[] {
return array.reverse();
}
// Example usage with number array
const numbers = [1, 2, 3, 4, 5];
const reversedNumbers = reverseArray<number>(numbers); // [5, 4, 3, 2, 1]
// Example usage with string array
const words = ['apple', 'banana', 'cherry'];
const reversedWords = reverseArray<string>(words); // ['cherry', 'banana', 'apple']
Example 2:
// Generic identity function
function identity<T>(arg: T): T {
return arg;
}
// Example usage with a number
const resultNumber: number = identity<number>(42);
// Example usage with a string
const resultString: string = identity<string>('Hello, generics!');
Example 3:
// Generic map function for arrays
function mapArray<T, U>(array: T[], mapper: (item: T) => U): U[] {
return array.map(mapper);
}
// Example usage with numbers
const numbers = [1, 2, 3, 4];
const squaredNumbers: number[] = mapArray(numbers, (num) => num * num);
// Example usage with strings
const words = ['apple', 'banana', 'cherry'];
const wordLengths: number[] = mapArray(words, (word) => word.length);
Example 4:
// Generic pair type and function
type Pair<T, U> = { first: T; second: U };
function createPair<T, U>(first: T, second: U): Pair<T, U> {
return { first, second };
}
// Example usage with numbers and strings
const numberAndStringPair: Pair<number, string> = createPair(42, 'Hello');
Example 5:
// Generic function for creating key-value pairs
function createKeyValuePair<T, U>(key: T, value: U): Record<T, U> {
const result: Record<T, U> = {} as Record<T, U>;
result[key] = value;
return result;
}
// Example usage with string key and number value
const keyValue = createKeyValuePair('count', 42);
2. Consumption: the consumption of generics are much higher
Let’s see examples where we use generics:
1. Arrays
const numbers = [1, 2, 3, 4, 5];
const reversedNumbers = reverseArray<number>(numbers);
2. Sets
const uniqueNumbers = createUniqueSet<number>([1, 2, 3, 3, 4, 5]);
// Set { 1, 2, 3, 4, 5 }
// Example usage with string set
const uniqueWords = createUniqueSet<string>(['apple', 'banana', 'apple', 'cherry']);
// Set { 'apple', 'banana', 'cherry' }
3. useRef ( React )
const [count, setCount] = useState<number>(0);
const [name, setName] = useState<string>("");
4. useState ( React )
const inputRef = useRef<string | null>(null);
I hope this article made you learn something knew about Generics. Thanks for your valuable time.