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

  • You are actually really close. However, there will be a few edge cases you will likely have to handle. In JS lots of things are objects, and you likely don't want them all to simply turn into strings. So you will probably have to enhance this with some more logic. But at it's simplest

    type RecursiveObject<T> = T extends Date ? never : T extends object ? T : never; 
    export type StringValues<TModel> = {
        [Key in keyof TModel]: TModel[Key] extends RecursiveObject<TModel[Key]> ? StringValues<TModel[Key]> : string;
    };
    

    Add in any special cases (Array? Other wrapper types?) that you need to handle in your code. Eventually we will get "not" handling in types, and this will be much simpler.