Extend an interface with new set of properties based on original interface with mapped types

Issue

Supposed I have the following interface:

interface Person {
    name: string;
    age: number;
    location: string;
}

I want to extend it to have 3 additional properties, one for each of the interfaces’ original properties.

So final outcome I want is:

interface Person {
    name: string;
    nameHidden: boolean;
    age: number;
    ageHidden: boolean;
    location: string;
    locationHidden: boolean;
}

I was looking into mapped types by TypeScript: https://www.typescriptlang.org/docs/handbook/2/mapped-types.html

But I am unable to figure out how I can achieve this behavior. It only shows examples of how to re-map the existing properties, but not add new ones on top of the existing ones.

Solution

You’ve said you don’t need it to be the same interface. That being the case, you can do this:

type WithFlags<T> = T & {
    [Key in keyof T as Key extends string ? `${Key}Hidden` : never]: boolean;
};

type PersonWithFlags = WithFlags<Person>;

Playground example

That creates a new type that is the passed-in type T plus a property for every property in it (at least the ones whose names extend string) with Hidden added to the name and the type boolean.

Titian Cernicova-Dragomir points out in a comment (thanks!) that we can avoid the conditional in that key mapping by using & string instead, which is a bit shorter:

type WithFlags<T> = T & {
    [Key in keyof T & string as `${Key}Hidden`]: boolean;
// −−−−−−−−−−−−−−−−^^^^^^^^^
};

Playground link

pilchard points out in a comment that the documentation has virtually this same example. They do the remapping like Titian, but in a different place:

type WithFlags<T> = T & {
    [Key in keyof T as `${Key & string}Hidden`]: boolean;
// −−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^^
};

Playground link

Answered By – T.J. Crowder

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published