I am currently facing an issue with a script that communicates with my API and returns the response to the caller. I am having trouble defining the type correctly for this operation.
The responses from the API always consist of a boolean flag, success
, indicating the state of the response, along with either a data
property containing the requested data or an errors
property containing error messages. Initially, I defined the type as follows:
type ApiResponse<T> = {
success: true;
data: T
} | {
success: false;
errors: string[]
}
However, when I tried to use this type to fetch an Event
, I encountered a problem. The returned data did not match the specified type due to how JSON encoding works over the network. Here is the structure of an Event
:
type Event = {
id: string;
name: string;
start: Date;
end: Date;
visibility?: {
host: boolean,
bands: boolean,
public: boolean
}
}
Below is an example response:
{
"success": true,
"data": [
{
"id": "70a1567f-5d5d-4c25-8622-f0cea9b67056",
"name": "Test Event",
"start": "2024-02-18T23:00:00.000Z",
"end": "2024-02-18T23:01:00.000Z",
},
]
}
The dates have been encoded as strings for easier transmission over the wire. After reviewing the JSON specification, I realized that all values should be stringified except for certain types like numbers, booleans, objects, etc.
To address this, I attempted to modify the ApiResponse
type so that the caller would be aware of the stringification and could process the data accordingly. However, I seem to be struggling with the type mapping. Here is my latest attempt:
type Stringified<T> = {
[key in keyof T]: T[key] extends number ? number :
T[key] extends boolean ? boolean :
T[key] extends Array<unknown> ? Stringified<T[key]> :
T[key] extends object ? Stringified<T[key]> :
string
}
type ApiResponse<T> = {
success: true;
data: Stringified<T>
} | {
success: false;
errors: string[]
}
However, this does not yield the expected result for ApiResponse<Event>
. Intellisense is interpreting the values as Stringified<Date>
instead of just string
, and the visibility
property is being treated as a string
instead of a
Stringified<{host: boolean, bands: boolean, public: boolean}>
. I suspect the issue may lie in the line T[key] extends object
, but I have tried various alternatives without success.
These errors arise when I attempt to convert the stringified values back into proper objects:
function hydrateEvent(rawEvent: Stringified<Event>){
const event: Event = {
id: rawEvent.id,
name: rawEvent.name,
start: new Date(rawEvent.start),
end: new Date(rawEvent.end)
};
if(rawEvent.visibility){
event.visibility = rawEvent.visibility;
}
return event;
}
Is it possible to achieve what I am attempting, or am I overlooking something obvious?