dumbmatter
dumbmatter

Reputation: 9683

Swap one TypeScript type with another inside an object

I have the following types:

type Target = number | undefined;

type MyObject = {
    some: string;
    properties: string;
    id: Target;
}

I want to come up with a generic way to replace Target with number, like:

type MyObjectTransformed = TargetToNumber<MyObject>;

/**
 * MyObjectTransformed is now:
 * 
 * {
 *     some: string;
 *     properties: string;
 *     id: number;
 * }
 */

It's easy if you know Target is always in the id field, I don't need help with that.

But what if Target could be anywhere? Like this should also work:

TargetToNumber<{
    some: string;
    other: Target;
    properties: string;
}>

And even worse... it also needs to work when it's nested! Like this should still replace Target with number:

TargetToNumber<{
    some: string;
    properties: string;
    nested: {
        arbitrarily: {
            deep: Target;
        };
    };
}>

If you're curious why I want to do such a strange thing, this is why I'm asking.

Upvotes: 1

Views: 956

Answers (1)

kaya3
kaya3

Reputation: 51092

Using a conditional type: if the arbitrary type T is A or a subtype of A, we replace it with B, otherwise if it's an object type we map the properties recursively, otherwise we leave the type as T.

type TargetToNumber<T> = SubstituteType<T, Target, number>;

type SubstituteType<T, A, B> =
    T extends A
    ? B
    : T extends {}
    ? { [K in keyof T]: SubstituteType<T[K], A, B> }
    : T;

If you only want to substitute exactly A and leave subtypes of A alone (e.g. never is always a subtype...) you can replace T extends A with [A, T] extends [T, A] to test that they are the same type.

Playground Link

Upvotes: 5

Related Questions