I'm currently working on a method that loops through an object and replaces key-value pairs where the value matches { _id: Types.ObjectId }
with a new key and maps the value to the string representation of _id
. For example:
{
"name": "test",
"image": {
"_id": "63162902546ac59fb830ccae",
"url": "https://..."
}
}
This object would be transformed into:
{
"name": "test",
"imageId": "63162902546ac59fb830ccae"
}
In addition, I want to specify keys that should not be modified. If I pass in image
as a parameter, the object will remain unchanged.
I have created a mapped type like this:
type TransformedResponse<T, R extends keyof T> = {
[K in keyof T as K extends R
? K
: T[K] extends { _id: Types.ObjectId }
? `${string & K}Id`
: K]: K extends R
? T[K]
: T[K] extends { _id: Types.ObjectId }
? string
: T[K];
}
This type seems to produce the expected results:
interface Example {
name: string;
image: {
_id: Types.ObjectId;
url: string;
};
}
type T1 = TransformedResponse<Example, 'name'>; // { name: string; imageId: string; }
type T2 = TransformedResponse<Example, 'image'>; // { name: string; image: { _id: Types.ObjectId; url: string; } }
The function for transforming the response looks like this:
export function ResponseTransformer<T>(
obj: T,
relations: (keyof T)[]
): TransformedResponse<T, keyof T> {
const transformedObj = {};
Object.keys(obj).forEach((key) => {
if (obj[key] && obj[key]._id && !relations.includes(key as keyof T)) {
transformedObj[`${key}Id`] = obj[key].toString();
} else {
transformedObj[key] = obj[key];
}
});
return transformedObj as TransformedResponse<T, keyof T>;
}
When calling this function like this:
const example = {
name: 'test',
image: {
_id: '63162902546ac59fb830ccae',
url: 'test',
},
};
const t1 = ResponseTransformer(example, ['name']);
The returned type is not T1
as expected but a generic response (
TransformedResponse<T, keyof T>
). This causes issues with type checking where the exact type T1
is required as the return type.
How can I make the function return the specific type?
Also, how can I make the generic argument R
optional in the type?