If you’re new to TypeScript, don’t miss Part 1 of this guide to get a foundational understanding before diving deeper into types and interfaces in Part 2
In this part of our TypeScript guide, we will dive into two powerful features that elevate the flexibility and maintainability of your code: Interfaces and Generics. Interfaces in TypeScript provide a way to define the structure of objects, ensuring that they follow a specific contract. This can help maintain consistency and improve code quality.
On the other hand, Generics allows you to create reusable, type-safe components that can work with any data type. By understanding and implementing these features, you can write more modular, scalable, and reliable applications. Let’s explore how interfaces and generics work in TypeScript and how you can use them to improve your development process.
Interfaces in TypeScript
An interface in TypeScript is a structure that defines the shape of an object. It specifies what properties and methods an object should have without implementing them. Interfaces help enforce consistency in your code by ensuring that objects follow a specific contract.
interface Movie {
title: string;
year: number;
genre?: string; // optional property
readonly review: number; // read-only property
}
// genre is optional property, so it can be omitted
const movie1: Movie = {
title: "The Lion King",
year: 1994,
review: 10,
};
const movie2: Movie = {
title: "Interstellar",
year: 2014,
genre: "Sci-Fi",
review: 9,
};
movie2.review = 5; // Error: Cannot assign to 'review' because it is a read-only property.
Generics in TypeScript:
Generics in TypeScript provide a way to create reusable and flexible components that can work with multiple types while ensuring type safety. They allow you to write a function, class, or interface that can operate on various data types without losing the benefits of static typing. Generics use angle brackets (<>) to define a placeholder for a type.
Here, T is a type parameter, representing any type passed during function invocation.
function identity<T>(value: T): T {
return value;
}
Generic Classes:
The MusicPlayer class is a generic class that accepts a type T for its songs array. The class defines a play method to remove and play the last song in the array.
class GenericNumber<NumType> {
zeroValue: NumType;
add: (x: NumType, y: NumType) => NumType;
constructor() {
this.zeroValue = {} as NumType;
this.add = (x: NumType, y: NumType) => x;
}
}
const myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = (x, y) => x + y;
console.log(myGenericNumber.add(3, 4)); // 7
const stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "";
stringNumeric.add = (x, y) => x + " " + y;
console.log(stringNumeric.add("hello", "world")); // helloworld
Generic Interfaces:
The Pair interface uses two type parameters, T and U, to define a pair of values (first and second).
Here, pair1 is a pair of number and string and pair2 is a pair of string and boolean.
interface Pair<T, U> {
first: T;
second: U;
}
const pair1: Pair<number, string> = {
first: 1,
second: "two",
};
const pair2: Pair<string, boolean> = {
Generic Functions:
The getLength function accepts an array of any type T and returns its length. The generic type T makes the function reusable for arrays of any data type.
interface Pair<T, U> {
first: T;
second: U;
}
const pair1: Pair<number, string> = {
first: 1,
second: "two",
};
const pair2: Pair<string, boolean> = {
Conclusion:
TypeScript’s interfaces and generics offer a robust way to handle complex data structures and enhance the flexibility of your code while maintaining type safety. Interfaces allow developers to define the structure of objects, enforcing consistency across your codebase, while generics provide a way to build reusable components that work with any data type. By incorporating these features into your TypeScript projects, you can improve code readability, reduce errors, and create more scalable applications.