Search code examples
typescripttypescript-typingstypescript-genericstype-safety

Typescript: change type of object properties and nested object properties to one type


Having this example:

interface Event {
  title:string;
  description:string;
  fromDate: Date;
  toDate: Date;
  location: {
    name: string;
    lat: number;
    long: number;
  }
}

Using a type something like PropertiesToString<Event> I expect to return this type:

{
  title:string;
  description:string;
  fromDate: string;
  toDate: string;
  location: {
    name: string;
    lat: string;
    long: string;
  }
}

The question is how do I create the PropertiesToString<T> type?

I've managed to create something that works but not for nested object. If i have an nested object instead of modifing the object properties to string, it sets the object to string.

This is my version which doesn't work for nested objects, because instead of changing the type of location properties to string , it changes the type for location itself to string:

export type RequestBody<T> = {
  [P in keyof T]: string;
};

Solution

  • type ToString converts a given type to string. PropertiesToString iterate over each key of the type passed and change its type to string using ToString. You can add other special cases that you want to handle in ToString using the type ternary operator.

    interface Event1 {
        title: string;
        description: string;
        fromDate: Date;
        toDate: Date;
        location: {
            name: string;
            lat: number;
            long: number;
      }
    }
    
    type ToString<T> = T extends Date
        ? string
        : T extends object
        ? PropertiesToString<T>
        : string
    
    type PropertiesToString<T> = {
        [K in keyof T]: ToString<T[K]>
    }
    
    type Generated = PropertiesToString<Event1>
    
    type X = PropertiesToString<Event1['location']>
    
    const x: Generated = {
        title: 'lorem',
        description: 'lorem',
        fromDate: 'lorem',
        toDate: 'lorem',
        location: {
            name: 'lorem',
            lat: 'lorem',
            long: 'lorem',
        }
    }
    

    Playground