Introduction#
TypeScript provides powerful type system features that go beyond basic types and interfaces. Among them, Mapped Types, Conditional Types, and Indexed Types enable developers to create more flexible and reusable type definitions.
This article explores these advanced TypeScript types, their use cases, and how they can help improve type safety and maintainability.
1. Mapped Types#
Mapped Types allow us to create new types by transforming the properties of an existing type. They are useful for defining variations of existing types, such as making fields optional or readonly.
Example: Making All Properties Readonly#
type User = {
id: number;
name: string;
email: string;
};
type ReadonlyUser = {
readonly [K in keyof User]: User[K];
};
// Equivalent to:
type ReadonlyUserEquivalent = {
readonly id: number;
readonly name: string;
readonly email: string;
};
Use Case: Creating immutable objects where properties should not be modified after initialization.
Example: Creating a Partial Type#
type PartialUser = {
[K in keyof User]?: User[K];
};
// Equivalent to:
type PartialUserEquivalent = {
id?: number;
name?: string;
email?: string;
};
Use Case: Useful for scenarios where only a subset of fields need to be updated.
2. Conditional Types#
Conditional Types allow types to be defined dynamically based on conditions. They follow the syntax:
T extends U ? X : Y
This means, "If T
extends U
, then use X
; otherwise, use Y
."
Example: Checking if a Type is a String#
type IsString<T> = T extends string ? "Yes" : "No";
type Result1 = IsString<string>; // "Yes"
type Result2 = IsString<number>; // "No"
Use Case: Used for defining utility types that behave differently based on input types.
Example: Extracting Function Return Types#
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
function getUser() {
return { id: 1, name: "Alice" };
}
type UserReturnType = ReturnType<typeof getUser>;
// { id: number; name: string; }
Use Case: Helps extract return types from functions, ensuring consistent typing across the codebase.
3. Indexed Types#
Indexed Types allow access to properties dynamically using keys. They are helpful when working with generic objects and ensuring safe property access.
Example: Extracting Property Types#
type User = {
id: number;
name: string;
email: string;
};
type UserIdType = User["id"]; // number
type UserNameType = User["name"]; // string
Use Case: Useful for ensuring a variable matches the type of a specific property in an object.
Example: Generic Indexed Access#
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { id: 1, name: "Alice", email: "alice@example.com" };
const userId = getProperty(user, "id"); // Type: number
const userEmail = getProperty(user, "email"); // Type: string
Use Case: Enables type-safe dynamic property access, reducing runtime errors.
4. Combining Mapped, Conditional, and Indexed Types#
By combining these features, we can create powerful type utilities.
Example: Making a Type Readonly Only for String Properties#
type ReadonlyStrings<T> = {
[K in keyof T]: T[K] extends string ? Readonly<T[K]> : T[K];
};
type Mixed = {
id: number;
name: string;
email: string;
};
type ReadonlyStringProps = ReadonlyStrings<Mixed>;
// Result:
// {
// id: number;
// readonly name: string;
// readonly email: string;
// }
Use Case: Helps enforce readonly constraints selectively based on property types.
Best Practices#
- Use mapped types for creating variations of an existing type (e.g.,
Readonly<T>
,Partial<T>
). - Use conditional types for defining types that depend on input types dynamically.
- Use indexed types to extract or validate property types within objects.
- Combine these features to create reusable and expressive type utilities.
Conclusion#
Mapped Types, Conditional Types, and Indexed Types are essential tools for writing flexible and scalable TypeScript code. They enable dynamic type transformations, enforce type constraints, and provide safer property access.
By mastering these advanced TypeScript features, developers can improve type safety and write more maintainable applications.