TypeScript Generics
TypeScript generics allow you to write reusable code components that can work with a variety of types rather than a single one. They provide type safety while maintaining flexibility. Think of them as templates or blueprints for your types. This allows you to catch type errors during compilation, leading to more robust code.
Let’s illustrate with a simple function to find the identity of a value:
function identity(arg: any): any {
return arg;
}
let myString: string = identity("hello");
let myNumber: number = identity(123);
This function works, but the any
type bypasses TypeScript’s type checking. If we mistakenly assign the result to the wrong type, we won’t get an error at compile time:
let myString: string = identity(123); // No error at compile time, runtime error!
This is where generics become valuable. We can rewrite the identity
function using a type parameter T
:
function identity<T>(arg: T): T {
return arg;
}
let myString: string = identity<string>("hello");
let myNumber: number = identity<number>(123);
let myStringError: string = identity<number>(123); // Compile-time error!
Now, TypeScript knows the return type based on the input type. Attempting to assign identity<number>(123)
to a string variable will result in a compile-time error, preventing potential runtime issues.
Generics are particularly powerful when working with complex data structures. Consider a function to log the length of an array:
function logLength<T>(arg: T[]): void {
console.log(arg.length);
}
logLength<string>(["hello", "world"]);
logLength<number>([1, 2, 3]);
Here, T
represents the type of elements within the array. The function works with arrays of strings, numbers, or any other type.
You can also use generics with interfaces and classes:
interface KeyValuePair<K, V> {
key: K;
value: V;
}
let stringPair: KeyValuePair<string, string> = { key: "name", value: "John" };
let numberPair: KeyValuePair<number, string> = { key: 1, value: "Jane" };
This example demonstrates a KeyValuePair
interface that can represent pairs of different types.
In conclusion, TypeScript generics offer a powerful mechanism for writing flexible and type-safe code. By using type parameters, you can create reusable components that adapt to various data types while ensuring type integrity at compile time, which ultimately leads to more reliable and maintainable applications.