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

JS uses canvas technology to imitate echarts bar chart

Canvas is a new tag in HTML5. You can use js to o...

MySQL method steps to determine whether it is a subset

Table of contents 1. Problem 2. Solution Option 1...

Initial settings after installing Ubuntu 16 in the development environment

The office needs Ubuntu system as the Linux devel...

Analysis of slow insert cases caused by large transactions in MySQL

【question】 The INSERT statement is one of the mos...

The use of vue directive v-bind and points to note

Table of contents 1. v-bind: can bind some data t...

Three ways to implement animation in CSS3

This is a test of the interviewee's basic kno...

CSS Sticky Footer Several Implementations

What is "Sticky Footer" The so-called &...

Detailed explanation of Bind mounts for Docker data storage

Before reading this article, I hope you have a pr...

Vue custom component implements two-way binding

Scenario: The interaction methods between parent ...

How to install Composer in Linux

1. Download the installation script - composer-se...

How to use Tencent slider verification code in Vue3+Vue-cli4 project

Introduction: Compared with traditional image ver...

React event mechanism source code analysis

Table of contents Principle Source code analysis ...

Docker practice: Python application containerization

1. Introduction Containers use a sandbox mechanis...

Use xshell to connect to the Linux server

Benefits of using xshell to connect to Linux We c...