2
  • Description : There is currently no Chinese translation of the latest official documents of TypeScript on the Internet, so there is such a translation plan. Because I am also a beginner in TypeScript, I cannot guarantee that the translation will be 100% accurate. If there are errors, please point out in the comment section;
  • translation content : The tentative translation content is TypeScript Handbook , and other parts of the translation document will be added later;
  • project address : TypeScript-Doc-Zh , if it helps you, you can click a star~

The official document address of this chapter: Mapped Types

Mapping type

Sometimes we don't want to rewrite the code, at this time we need to create another type based on one type.

Index signatures are used to declare types for attributes that have not been declared in advance, while mapping types are constructed based on the syntax of index signatures.

type OnlyBoolsAndHorses = {
    [key: string]: boolean | Horse;
};
const conforms: OnlyBoolsAndHorses = {    
    del: true,
    rodney: false,
};

The mapping type is also a generic type. It uses PropertyKey (attribute key) (usually keyof ) to traverse all the keys to create a new type:

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

In this example, OptionsFlags will accept Type and change their values to boolean values.

type FeatureFlags = {
    darkMode: () => void;
    newUserProfile: () => void;
};

type FeatureOptions = OptionsFlags<FeatureFlags>;
           ^
      /* 
      type FeatureOptions = {
            darkMode: boolean;
            newUserProfile: boolean;
      }     
      */ 

Mapping modifier

There are two additional modifiers available when mapping, namely readonly and ? , which are used to declare the read-only and optional properties of the attribute respectively.

To remove or add modifiers, just add the prefix - or + to the modifier. If no prefix is added, + will be used by default.

// 移除类型中属性的只读性
type 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;
       }     
       */ 
// 移除类型中属性的可选性
type 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;
   }    
   */ 

Implement key remapping through as

In TypeScript4.1 or higher, you can use the as clause in the mapping type to implement key remapping:

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

You can use such as the 161b1be92a7062 template literal type to create new attribute names from the original attribute names:

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 generate never type by condition type to filter out certain keys:

// 移除 kind 属性
type Exclude<T,U> = T extends U ? never : T
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;
      }
      */         

Not only string | number | symbol , but any union type can be mapped:

type EventConfig<Events extends { kind: string }> = {
    [E in Events as E['kind']]: (event: E) => void;
}

type SqureEvent = { kind: 'squre', x: number, y: number };
type CircleEvent = { kind: 'circle', radius: number };

type Config = EventConfig<SqureEvent | CricleEvent>
       /**
       type Config = {
            squre: (event: SqureEvent) => void;
            circle: (event: CircleEvent) => void;
       }
       /    

Further application of mapping types

The mapping type can also be used in conjunction with other features introduced in this chapter (type manipulation). For example, following is a use condition type mapping type , whether a literal is set according to the object true properties pii , it will return true or false :

type ExtractPII<Type> = {
    [Property in keyof Type]: Type[Property] extends { pii: true } ? true : false
};

type DBFileds = {
    id: { format: 'incrementing' };
    name: { type: string; pii: true };
};

type ObjectsNeedingGDPRDeletion = ExtractPII<DBFileds>;
                ^
            /*
            type ObjectsNeedingGDPRDeletion = {
                id: false;
                name: true;
            }
            */        

Chor
2k 声望5.9k 粉丝