Search code examples
typescriptmapped-types

How to use mapped types to map a subset of properties in a nested generic type


I have a type like so:

type Thing = {
  name: string;
  deliveryDate: string;
  stuff: {
    place: string;
    datedContacted: string;
  }
}

That I want to create a mapped type with the same structure out of like so:

type FormThing = {
  name: string;
  deliveryDate: Moment;
  stuff: {
    place: string;
    datedContacted: Moment;
  }
}

I'm looking at TypeScript's "Mapped Types" here: https://www.typescriptlang.org/docs/handbook/2/mapped-types.html, but their examples seem to mostly go over how to convert all the properties of a type to another, not a subset.

I've tried to create a generic typing like so:

type DateFields = {
  deliveryDate: string;
  dateContacted: string;
}

export type Form<T> = T & {
  [Property in keyof DateFields]: Moment;
};

But it just doesn't seem to work as expected. Ideally I make it generic, so I can create a typed method that could handle the conversion of any sublevel of this nested object independently from T to Form<T>.


Solution

  • You can achieve this deep mapped type by first changing DateFields to union of string literals, and then by creating a recursive mapped type

    type Thing = {
        name: string;
        deliveryDate: string;
        stuff: {
            place: string;
            dateContacted: string;
        }
    }
    
    type DateFields = 'deliveryDate' | 'dateContacted';
    
    
    type Form<T, Fields> = {
        [K in keyof T]: K extends Fields ? Moment : T[K] extends object ? Form<T[K], Fields> : T[K];
    }
    

    Inspect it in this TypeScript playground