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