I am trying to create a generic type for my API responses. The general form is that I have different formats depending on what API I'm calling, but they follow a general structure.
The main content of the response will be in response.result.content
, so I have added that as a generic type. I can use types such as ApiResponseSuccess<MyType>
to know that an object with type MyType
will be in response.result.content
.
However, some APIs will have other properties in response.result
that I want to be able to access. I could put something like: [key: string]: any;
to get rid of any errors and warnings like below:
//my custom type
type ApiResponseSuccess<T, O = {}> = {
message: "Success";
success: true;
result: {
content: T;
[key: string]: any;
};
};
and not have to worry about that, but I would like to type this a bit more strongly. So I am trying to do something like below with key remapping, passing in a second generic type optionally to spread out its properties and add its type to the response type.
// General API Types
//taken directly from the typescript documentation
type MappedTypeWithNewProperties<O = {}> = {
[K in keyof O as string]: O[K] //works without errors
}
//my custom type
type ApiResponseSuccess<T, O = {}> = {
message: "Success";
success: true;
result: {
content: T;
[K in keyof O as string]: O[K]; //causes the following errors
// Member '[K in keyof' implicitly has an 'any' type
// ']' expected.
// Cannot find name 'O', Cannot find name 'K'
};
};
And to use the type,
type AdditionalProps = {
pageSize: number;
totalPages: number;
currentPage: number;
}
const response = ApiResponseSuccess<MyType[], AdditionalProps>;
to dynamically create a type like the following:
type ApiResponseSuccess<MyType[], AdditionalProps> = {
message: "Success";
success: true;
result: {
content: MyType[];
pageSize: number;
totalPages: number;
currentPage: number;
}
}
However, trying to follow the documentation, I am not able to write types with key remapping. What is wrong with my code, and what do I need to do to achieve such behaviour?
CodePudding user response:
Try the following snippet. It should work and it's simpler.
type ApiResponseSuccess<T, O = {}> = {
message: "Success";
success: true;
result: {
content: T;
} & O;
};
CodePudding user response:
I have figured out what was the problem. The linter errors were not accurate. The issue was the following error:
A mapped type may not declare properties or methods. ts(7061)
So the problem was that I was declaring content: T
next to the mapped type, which wasn't allowed.
However, I do need that content: T
to represent my type correctly. I have written the following type to avoid this issue:
type ApiResponseSuccess<T, O extends { content: T } = { content: T }> = {
message: "Success";
success: true;
result: {
[K in keyof O]: O[K];
};
};
Now, the optional type O
extends { content: T }
and has { content: T }
as its default value. With that, the resulting type would contain content: T
in K in keyof O
.