Reputation: 9683
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
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.
Upvotes: 5