Advanced types in TypeScript provide developers with powerful tools to create more robust and maintainable applications. By leveraging features such as union types, intersection types, mapped types, and conditional types, developers can model complex data structures and enforce stricter type checks. Below, we explore several real-world use cases for these advanced types, highlighting practical examples, best practices, and common pitfalls.
Union types allow a variable to hold multiple types, which is particularly useful when dealing with APIs that may return different types of data based on certain conditions.
type ApiResponse = { success: true; data: string } | { success: false; error: string };
function handleResponse(response: ApiResponse) {
if (response.success) {
console.log(response.data);
} else {
console.error(response.error);
}
}
In this example, the `ApiResponse` type can either represent a successful response with data or an error response. This structure helps in handling different outcomes effectively.
Intersection types combine multiple types into one, allowing for more complex data structures. This is useful when you want to create objects that share properties from multiple types.
interface User {
id: number;
name: string;
}
interface Admin {
adminLevel: number;
}
type AdminUser = User & Admin;
const admin: AdminUser = {
id: 1,
name: 'Alice',
adminLevel: 5
};
Here, the `AdminUser` type combines properties from both `User` and `Admin`, ensuring that any object of type `AdminUser` contains all required fields.
Mapped types enable the creation of new types by transforming existing ones. This is particularly useful for creating variations of types without repeating code.
type ReadOnly = {
readonly [K in keyof T]: T[K];
};
interface User {
id: number;
name: string;
}
type ReadOnlyUser = ReadOnly;
const user: ReadOnlyUser = { id: 1, name: 'Bob' };
// user.id = 2; // Error: Cannot assign to 'id' because it is a read-only property
In this case, the `ReadOnly` mapped type creates a new type that makes all properties of `User` read-only, preventing accidental mutations.
Conditional types allow for type selection based on conditions, which is useful for creating flexible and reusable components.
type IsString = T extends string ? 'Yes' : 'No';
type Test1 = IsString; // 'Yes'
type Test2 = IsString; // 'No'
This example demonstrates how conditional types can be used to derive types based on other types, enhancing type safety and clarity.
By understanding and applying advanced types effectively, developers can enhance the quality and maintainability of their TypeScript applications, leading to fewer bugs and a more predictable codebase.