5

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

1 Answer 1

6

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

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.