Reputation: 6844
I have the following code;
class Transform<T> {
constructor(private value: T) {}
}
class Test<T extends object> {
constructor(private a: T) {}
transform(): { [K in keyof T]: Transform<T[K]> } {
// functionality omitted
return this.a as any;
}
}
const test = new Test({
a: '',
b: {
c: '',
d: {
v: ''
}
}
})
const transformed = test.transform();
The issue is that the transform
method return type is:
const transformed: {
a: Transform<string>;
b: Transform<{
c: string;
d: {
v: string;
};
}>;
}
But what I want is that every key will be of type Transform:
const transformed: {
a: Transform<string>;
b: Transform<{
c: Transform<string>;
d: Transform<{
v: Transform<string>;
}>;
}>;
}
There is a way to achieve it with TS?
Upvotes: 0
Views: 120
Reputation: 25790
Sure. Try a conditional recursive type like this:
type Transformed<T> = {
[K in keyof T]:
T[K] extends object
? Transform<Transformed<T[K]>>
: Transform<T[K]>
};
Use it as the return type for transform()
.
class Test<T extends object> {
constructor(private a: T) {}
transform(): Transformed<T> {
// functionality omitted
return this.a as any;
}
}
The result:
/**
* @returns
*
* {
* a: Transform<string>;
* b: Transform<{
* c: Transform<string>;
* d: Transform<{
* v: Transform<string>;
* }>;
* }>;
* }
*
*/
const transformed = test.transform();
Note: Some IDEs (like Visual Studio Code) may not show the expanded return type. If you want to make sure that my solution is indeed what you are looking for, use a helper type like Compact
so that the return type appears in full glory.
/**
* @see https://github.com/microsoft/TypeScript/issues/14829#issuecomment-320754731
*/
type Compact<T> = {} & { [P in keyof T]: T[P] };
type Transformed<T> = Compact<{
[K in keyof T]:
T[K] extends object
? Transform<Transformed<T[K]>>
: Transform<T[K]>
}>;
Now it will look exactly like what you asked for. Hope that helps!
Upvotes: 1