DEV Community

loading...
Cover image for Typescript - Tips & Tricks - Mapped Types

Typescript - Tips & Tricks - Mapped Types

puppo profile image Luca Del Puppo ・2 min read

In some cases, we need to manipulate some types to create new types. In these cases, we have to use the Mapped Types.
Let's start explaining the structure of the mapped types.

type MappedTypeExample = {
    [Key in "x" | "y" | "z"]: number;
};
Enter fullscreen mode Exit fullscreen mode

As you can see, a mapped type is a type composed of defined keys and their values, but the power of the mapped types goes out when we want to joke with a generic type.

type Point = {
    readonly x: number;
    y: number;
    z?: number;
};

type MappedType<T> = {
    [K in keyof T]: T[K];
};

type mappedPoint = MappedType<Point>;
/**
 type mappedPoint = {
    readonly x: number;
    y: number;
    z?: number | undefined;
}
*/
Enter fullscreen mode Exit fullscreen mode

In this simple example, I show you how to replicate the same object using the mapped type.
Now let's add two simple bits to transform our type into new "different" types. The mapped type permits you to add(+) or remove(-) the read-only modifier from your type but also to add(+) or remove(-) the optional modifier.
I leave you two simple examples to show these cases.

type MappedPointPlusModifier<T> = {
    +readonly [K in keyof T]: T[K];
};

type mappedPointPlusModifier = MappedPointPlusModifier<Point>;
/**
type mappedPointPlusModifier = {
    readonly x: number;
    readonly y: number;
    readonly z?: number | undefined;
}
*/

type MappedPointMinusModifier<T> = {
    [K in keyof T]-?: T[K];
};
type mappedPointMinusModifier = MappedPointMinusModifier<Point>;
/**
type mappedPointMinusModifier = {
    readonly x: number;
    y: number;
    z: number;
}
*/
Enter fullscreen mode Exit fullscreen mode

N.B. The add(+) modifier isn't indicated in common cases because it is implied

The mapped types are a powerful feature to manipulate our types and they are used by the typescript team to expose to us some advanced types, but this is a topic of a future post.
Before ending I leave you another example to show what you can do with the mapped type.
In this example, I transform the Point type to another Point type where the fields aren't exposed directly but they are wrapped by a "get" and "set" method.

type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type Setters<T> = {
  [K in keyof T as `set${Capitalize<string & K>}`]: (value: T[K]) => void;
};
type WithGetterAndSetter<T> = Getters<T> & Setters<T>;

type Point = {
  x: number;
  y: number;
};

type WithGetterAndSetterPoint = WithGetterAndSetter<Point>;
/**
type WithGetterAndSetterPoint = {
  getX(): number;
  setX(value: number): void;
  getY(): number;
  setY(value: number): void;
}
*/
Enter fullscreen mode Exit fullscreen mode

It's all for today!
See you soon guy!

Discussion (0)

pic
Editor guide