Developed By
Gautam Kumar - Full stack developer
DEEP DIVE INTO
Generics
in JavaScript provide a way to write reusable and type-safe code that works with different data types. While JavaScript itself doesn't have built-in support for generics, modern JavaScript dialects like TypeScript offer support for generic
programming. In this deep dive, we'll focus on generics in TypeScript, as it's a widely used JavaScript superset
with strong typing.
Generics
allow you to define functions, classes, and interfaces that can work with a range of data types while preserving type safety. Here's a simple example of a generic function that swaps the values of two variables:
javascriptfunction swap(a: T, b: T): [T, T] {
return [b, a];
}
const swapped = swap("hello", "world"); // swapped is ['world', 'hello']
In this example, T is a generic
type parameter that represents the data type passed to the swap function
. This function can work with any data type while ensuring that the output maintains the same data types as the inputs.
You can restrict the range of data types that can be used with generics
using constraints. For instance, you can require that a generic
type parameter extends a particular interface
or class. Here's an example:
javascriptinterface Shape {
area(): number;
}
class Circle implements Shape {
constructor(private radius: number) {}
area() {
return Math.PI * this.radius ** 2;
}
}
class Square implements Shape {
constructor(private side: number) {}
area() {
return this.side ** 2;
}
}
function calculateArea(shape: T): number {
return shape.area();
}
const circle = new Circle(5);
const square = new Square(4);
const circleArea = calculateArea(circle); // 78.54
const squareArea = calculateArea(square); // 16
In this example, the calculateArea
function uses a generic
type parameter T constrained to types that extend the Shape interface.
You can create generic
classes that work with different data types. Here's an example of a generic
Stack class:
javascriptclass Stack {
private items: T[] = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
}
const numberStack = new Stack();
numberStack.push(1);
numberStack.push(2);
const poppedNumber = numberStack.pop(); // 2
const stringStack = new Stack();
stringStack.push("hello");
stringStack.push("world");
const poppedString = stringStack.pop(); // "world"
In this example, the Stack class is generic, allowing you to create instances of the stack with different data types.
You can define generic
interfaces to specify the structure of objects or classes that work with various data types. Here's an example of a generic
Comparer interface:
javascriptinterface Comparer {
compare(a: T, b: T): number;
}
const numberComparer: Comparer = {
compare(a, b) {
return a - b;
}
};
const stringComparer: Comparer = {
compare(a, b) {
return a.localeCompare(b);
}
};
const numResult = numberComparer.compare(5, 3); // 2
const strResult = stringComparer.compare("apple", "banana"); // -1
In this example, the Comparer interface is generic, allowing you to define different comparers for different data types.
TypeScript provides utility types like Partial, Pick, and Record that work well with generics
. You can use these utility types to manipulate and transform types more easily. For example:
javascripttype PartialPoint = Partial>;
const partialPoint: PartialPoint = { x: 10 };
Here, we use Partial and Record with a generic
type T to create a type for a partial point with optional x and y properties.
Generics
in TypeScript enable you to write highly flexible and type-safe code that works with different data types, making your code more reusable and easier to maintain. They are widely used in libraries, frameworks, and other codebases
to provide generic
solutions to a variety of programming challenges.