Mapped types in TypeScript allow you to create new types by transforming existing ones. They are particularly useful for creating types that are variations of other types, such as making all properties optional or readonly. This topic will cover the basics of mapped types, provide practical examples, and include exercises to reinforce the concepts.

Key Concepts

  1. Definition: Mapped types use a syntax that iterates over the properties of an existing type and applies a transformation to each property.
  2. Syntax: The basic syntax for a mapped type is:
    type MappedType<T> = {
        [P in keyof T]: Transformation;
    };
    
  3. Common Use Cases:
    • Making all properties optional
    • Making all properties readonly
    • Transforming property types

Practical Examples

Example 1: Making All Properties Optional

Let's start with a simple example where we make all properties of a type optional.

type Person = {
    name: string;
    age: number;
    address: string;
};

type OptionalPerson = {
    [P in keyof Person]?: Person[P];
};

// Usage
const person1: OptionalPerson = {}; // Valid
const person2: OptionalPerson = { name: "Alice" }; // Valid

Example 2: Making All Properties Readonly

In this example, we will create a mapped type that makes all properties of a type readonly.

type ReadonlyPerson = {
    [P in keyof Person]: Readonly<Person[P]>;
};

// Usage
const person3: ReadonlyPerson = { name: "Bob", age: 30, address: "123 Main St" };
// person3.name = "Charlie"; // Error: Cannot assign to 'name' because it is a read-only property.

Example 3: Transforming Property Types

Here, we will create a mapped type that transforms all properties to a different type.

type StringifiedPerson = {
    [P in keyof Person]: string;
};

// Usage
const person4: StringifiedPerson = { name: "Dave", age: "40", address: "456 Elm St" };

Exercises

Exercise 1: Create a Mapped Type to Make Properties Nullable

Create a mapped type Nullable<T> that makes all properties of a type T nullable.

type Nullable<T> = {
    [P in keyof T]: T[P] | null;
};

// Test it with the Person type
type NullablePerson = Nullable<Person>;

// Usage
const person5: NullablePerson = { name: null, age: 25, address: null };

Exercise 2: Create a Mapped Type to Add a Prefix to Property Names

Create a mapped type Prefixed<T, Prefix> that adds a prefix to all property names of a type T.

type Prefixed<T, Prefix extends string> = {
    [P in keyof T as `${Prefix}${P & string}`]: T[P];
};

// Test it with the Person type
type PrefixedPerson = Prefixed<Person, "prefix_">;

// Usage
const person6: PrefixedPerson = { prefix_name: "Eve", prefix_age: 35, prefix_address: "789 Pine St" };

Common Mistakes and Tips

  • Forgetting to use keyof: When creating mapped types, always ensure you use keyof to iterate over the keys of the type.
  • Incorrect transformations: Ensure that the transformations applied to the properties are valid and make sense for the use case.
  • Complex transformations: For more complex transformations, consider breaking them down into smaller, more manageable mapped types.

Conclusion

Mapped types are a powerful feature in TypeScript that allow you to create new types by transforming existing ones. They are particularly useful for creating variations of types, such as making properties optional, readonly, or transforming their types. By understanding and utilizing mapped types, you can write more flexible and reusable TypeScript code.

In the next topic, we will explore Conditional Types, which allow you to create types based on conditions, further enhancing the flexibility and power of TypeScript's type system.

© Copyright 2024. All rights reserved