Typescript - Tips & Tricks - Mapped Types

In some cases, we need to manipulate some types to create new types. In these cases, we have to use...

#typescript#webdev
Luca Del PuppoLuca Del Puppo
Article Series · Part 19 of 21TypeScript - Tips & Tricks
View full series (21)
  1. #18Previous
    infer keyword
  2. #19Reading now
    Mapped Types
  3. #20Up next
    Advanced Types
Browse all 21 parts in this series
  1. #1typeof
  2. #2Literal Types
  3. #3keyof
  4. #4Union and Intersection
  5. #5Overloaded Functions
  6. #6this parameter
  7. #7any vs. unknown
  8. #8Optional modifier
  9. #9Index Signature
  10. #10readonly modifier
  11. #11Readonly Array
  12. #12Tuple
  13. #13Non-null assertion operator
  14. #14Never type
  15. #15Assert Function
  16. #16Type Guard
  17. #17Conditional Type
  18. #18infer keyword
  19. #19Mapped TypesCurrent
  20. #20Advanced Types
  21. #21Rest Parameters & Rest Arguments

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;
};

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;
}
*/

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;
}
*/

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;
}
*/

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

Luca Del Puppo

Written by Luca Del Puppo

Run, Code & Learn

AI Native Engineer at Nearform based in Povegliano (TV), Italy. Google Developer Expert in Web Technologies, GitKraken Ambassador, and Codemotion Ambassador.