Reputation: 8091
I need to recursively get through the data structure and create a type
that has some of the fields changed to a different type, based on a condition.
Based on the following structure, I need to create a type (Result
) where all A
types are replaced with B
types.
class A{}
class B{}
const data = {
propA:new AA,
propB:true,
nested:{
propC:new AA,
propD:false
}
}
// something like:
type Replace<typeof data, A, B>
// result
type Result = {
propA:B,
propB:boolean,
nested:{
propC:B
propD:boolean
}
}
Upvotes: 3
Views: 1900
Reputation: 1602
This can be achieved with conditional, recursive types:
class A {
ClassPropA!: string
}
class B {
ClassPropB!: string
}
const data = {
propA: new A(),
propB: true,
nested:{
propC: new A(),
propD:false
}
}
type Replace<T, A, B> = T extends object
? { [key in keyof T]: T[key] extends A ? B : Replace<T[key], A, B> }
: T;
// type Result = {
// propA: B,
// propB: boolean,
// nested:{
// propC: B
// propD: boolean
// }
//
// }
type Result = Replace<typeof data, A, B>;
Upvotes: 1
Reputation: 15116
You can do this with a mapped type, but keep in mind that matching is based on object structure rather than class name, so an object from a class C{}
will also get converted when you're targeting A
.
The Replace
type can be defined as
type Replace<T, From, To> = T extends (...args: any[]) => any ? T : {
[K in keyof T]:
[T[K], From] extends [From, T[K]] ? To : Replace<T[K], From, To>
}
The first condition is to preserve any methods/function properties as mapped types will convert these to {}
. The mapped type itself processes each key, and checks whether both value extends the From
type and the From
type extends the value type, to ensure equality. If both are equal, the value gets replaced with To
, and otherwise Replace
is called recursively.
Here's an example conversion:
class A{}
class B{b = 42}
class C{}
const data = {
propA: new A(),
propBool: true,
propC: new C(),
nested:{
propA: new A(),
propBool: false
},
f: () => 42
}
type Result = Replace<typeof data, A, B>
// type Result = {
// propA: B;
// propBool: boolean;
// propC: B;
// nested: {
// propA: B;
// propBool: boolean;
// };
// f: () => number;
// }
Upvotes: 3