Home > database >  Replace a specific type with another type in a nested object typescript
Replace a specific type with another type in a nested object typescript

Time:05-11

I want to replace a specific type with another type, as so:

interface Human {
  name: string;
  age: number;
  isMarried: boolean;
  animals: [
    { name: string, age: number}, 
    { name: string, age: number}
  ]
}

type HumanString = ReplaceTypes<Human, number, string>

/*
HumanString {
  name: string;
  age: string;
  isMarried: boolean;
  animals: [
    { name: string, age: string}, 
    { name: string, age: string}
  ]
}
*/

Where the has the following interface ReplaceTypes<OriginalType, OldType, NewType> Oldtype should be replaced in all nested occurances for NewType

CodePudding user response:

You can do that with a recursive mapped type that looks like this:

type ReplaceTypes<ObjType extends object, FromType, ToType> = {
    [KeyType in keyof ObjType]: ObjType[KeyType] extends object
        ? ReplaceTypes<ObjType[KeyType], FromType, ToType>  // Recurse
        : ObjType[KeyType] extends FromType    // Not recursing, need to change?
            ? ToType                           // Yes, change it
            : ObjType[KeyType];                // No, keep original
}

So for instance, if you had example: HumanString, example.age and example.animals[number].age would both be string instead of number.

It looks to see if the type of each property extends object and if so, recurses; otherwise, it looks to see if the type extends the "from" type and replaces it if so.

Playground link

All of those repeated instances of ObjType[KeyType] get tiresome, so you could split it up into two parts:

type ReplaceType<Type, FromType, ToType> =
    Type extends object
    ? ReplaceTypes<Type, FromType, ToType>
    : Type extends FromType
        ? ToType
        : Type;

type ReplaceTypes<ObjType extends object, FromType, ToType> = {
    [KeyType in keyof ObjType]: ReplaceType<ObjType[KeyType], FromType, ToType>;
}

Playground link

If you wanted to be able to do this with FromType being an object, type, it should work if you change the order:

type ReplaceType<Type, FromType, ToType> =
    Type extends FromType                       // FromType?
    ? ToType                                    // Yes, replace it
    : Type extends object                       // Recurse?
        ? ReplaceTypes<Type, FromType, ToType>  // Yes
        : Type;                                 // No, leave it alone

type ReplaceTypes<ObjType extends object, FromType, ToType> = {
    [KeyType in keyof ObjType]: ReplaceType<ObjType[KeyType], FromType, ToType>;
}

Playground link

  • Related