TypeScript Mapping Type Details

TypeScript Mapping Type Details

Preface:

The official documentation of TypeScript has been updated a long time ago, but the Chinese documentation I can find is still in an older version. Therefore, some of the new and revised chapters were translated and sorted out.

This translation is compiled from the "Mapped Types" chapter in the TypeScript Handbook.

This article is not strictly translated according to the original text, and some explanations and supplements are also provided.

1. Mapped Types

Sometimes, a type needs to be based on another type, but you don't want to make a copy. In this case, you can consider using a mapping type.

Mapping types are based on the syntax of index signatures. Let's review the index signatures first:

// When you need to declare the type of the property in advance type OnlyBoolsAndHorses = {
  [key: string]: boolean | Horse;
};
 
const conforms: OnlyBoolsAndHorses = {
  del: true,
  rodney: false,
};


The mapping type is a generic type that uses PropertyKeys union type, where PropertyKeys is usually created through keyof , and then loops through the key names to create a type:

type OptionsFlags<Type> = {
  [Property in keyof Type]: boolean;
};


In this example, OptionsFlags will iterate over all properties of Type and set them to Boolean type.

type FeatureFlags = {
  darkMode: () => void;
  newUserProfile: () => void;
};
 
type FeatureOptions = OptionsFlags<FeatureFlags>;
// type FeatureOptions = {
// darkMode: boolean;
// newUserProfile: boolean;
// }


2. Mapping Modifiers

When using a mapped type, there are two additional modifiers that may be used, one is readonly, which is used to set the property to be read-only, and the other is?, which is used to set the property to be optional.

You can remove or add these modifiers by prefixing them with - or +. If no prefix is ​​given, it is equivalent to using the + prefix.

// Delete the read-only attribute type in the attribute CreateMutable<Type> = {
  -readonly [Property in keyof Type]: Type[Property];
};
 
type LockedAccount = {
  readonly id: string;
  readonly name: string;
};
 
type UnlockedAccount = CreateMutable<LockedAccount>;

// type UnlockedAccount = {
// id: string;
// name: string;
// }
// Delete the optional attribute type in the attribute Concrete<Type> = {
  [Property in keyof Type]-?: Type[Property];
};
 
type MaybeUser = {
  id: string;
  name?: string;
  age?: number;
};
 
type User = Concrete<MaybeUser>;
// type User = {
// id: string;
// name: string;
//age: number;
// }

3. Key Remapping via as

In TypeScript 4.1 and later, you can remap keys in mapped types using the as statement:

type MappedTypeWithNewProperties<Type> = {
    [Properties in keyof Type as NewKeyType]: Type[Properties]
}


For example, you can use Template Literal Types to create a new property name based on a previous property name:

type Getters<Type> = {
    [Property in keyof Type as `get${Capitalize<string & Property>}`]: () => Type[Property]
};
 
interface Person {
    name: string;
    age: number;
    location: string;
}
 
type LazyPerson = Getters<Person>;

// type LazyPerson = {
// getName: () => string;
// getAge: () => number;
// getLocation: () => string;
// }

You can also use the conditional type to return a never to filter out certain attributes:

// Remove the 'kind' property
type RemoveKindField<Type> = {
    [Property in keyof Type as Exclude<Property, "kind">]: Type[Property]
};
 
interface Circle {
    kind: "circle";
    radius: number;
}
 
type KindlessCircle = RemoveKindField<Circle>;

// type KindlessCircle = {
// radius: number;
// }

You can also iterate over any union type, not just string | number | symbol , but any type of union:

type EventConfig<Events extends { kind: string }> = {
    [E in Events as E["kind"]]: (event: E) => void;
}
 
type SquareEvent = { kind: "square", x: number, y: number };
type CircleEvent = { kind: "circle", radius: number };
 
type Config = EventConfig<SquareEvent | CircleEvent>
// type Config = {
// square: (event: SquareEvent) => void;
// circle: (event: CircleEvent) => void;
// }

4. Further Exploration

Mapping types can also be used with other functions. For example, this is a mapping type that uses conditional types and returns true or false depending on whether the object has a pii attribute:

type ExtractPII<Type> = {
  [Property in keyof Type]: Type[Property] extends { pii: true } ? true : false;
};
 
type DBFields = {
  id: { format: "incrementing" };
  name: { type: string; pii: true };
};
 
type ObjectsNeedingGDPRDeletion = ExtractPII<DBFields>;
// type ObjectsNeedingGDPRDeletion = {
// id: false;
// name: true;
// }

You may also be interested in:
  • Explanation of TypeScript common types and application examples
  • Teach you to use typescript types to calculate Fibonacci
  • Detailed explanation of TypeScript's basic types
  • TypeScript Enumeration Type
  • Introduction to TypeScript basic types
  • Let's learn TypeScript types together

<<:  Detailed process of upgrading gcc (version 10.2.0) under CentOS7 environment

>>:  How to display web pages properly in various resolutions and browsers

Recommend

Code analysis of user variables in mysql query statements

In the previous article, we introduced the MySQL ...

Detailed steps to install MySQL 5.6 X64 version under Linux

environment: 1. CentOS6.5 X64 2.mysql-5.6.34-linu...

WeChat applet implements a simple dice game

This article shares the specific code of the WeCh...

How many times will multiple setStates in React be called?

Table of contents 1. Two setState, how many times...

TCP socket SYN queue and Accept queue difference analysis

First we must understand that a TCP socket in the...

How to install PostgreSQL11 on CentOS7

Install PostgreSQL 11 on CentOS 7 PostgreSQL: The...

Detailed explanation of small state management based on React Hooks

Table of contents Implementing state sharing base...

Detailed explanation of JavaScript primitive data type Symbol

Table of contents Introduction Description Naming...

How to reduce the memory and CPU usage of web pages

<br />Some web pages may not look large but ...

Ansible automated operation and maintenance deployment method for Linux system

Ansible is a new automated operation and maintena...

MySQL pessimistic locking and optimistic locking implementation

Table of contents Preface Actual Combat 1. No loc...

CSS3 realizes the effect of triangle continuous enlargement

1. CSS3 triangle continues to zoom in special eff...

Let's take a look at some powerful operators in JavaScript

Table of contents Preface 1. Null coalescing oper...

How to obtain a permanent free SSL certificate from Let's Encrypt in Docker

1. Cause The official cerbot is too annoying. It ...