I have the following type system for a generic ApiResponse<DataT = void, ExtraT = void>
where you can either give it generics or not to produce different results:
export interface ApiResponseBase {
Success: boolean;
}
export interface ApiResponseDataAndMaybeCount<DataT> {
Data: DataT;
Count?: number;
}
export type ApiResponseMaybeWithData<DataT = void> = DataT extends void ?
ApiResponseBase
:
ApiResponseBase &
ApiResponseDataAndMaybeCount<DataT>;
export type ApiResponse<DataT = void, ExtraT = void> = ExtraT extends void ?
ApiResponseMaybeWithData<DataT>
:
ApiResponseMaybeWithData<DataT> &
ExtraT;
The idea was to create a type that has Data keys but only when you need them, and extra keys but only when you know what they are and can define them before hand in other types.
Example type outputs:
ApiResponse<> (specify no generics)
produces
{
Success: boolean;
}
ApiResponse<string, {extra: string}> (specify both DataT and ExtraT)
produces
{
Success: boolean;
Data: string;
extra: string;
}
I wanted to make a generic get function that you could specific the type of the Data in the ApiResponse:
export async function getApiResponse<T>(url: string): Promise<T | undefined> {
const response = await get<ApiResponse<T>>(url);
if (response?.value && response.value.Success && response.value.Data) {
return response?.value.Data;
}
return;
}
The problem is that the type of response is globalThis.Ref<globalThis.ApiResponseMaybeWithData<T>> | undefined
and when trying to access response.value.Data
I get the following error:
Property 'Data' does not exist on type 'ApiResponseBase | (ApiResponseBase & ApiResponseDataAndMaybeCount<T>)'.
Property 'Data' does not exist on type 'ApiResponseBase'.ts(2339)
I know that T is getApiResponse<T>()
is required, but there is nothing stopping T
from being void
so the compiler can’t guarantee the outcome of ApiResponseMaybeWithData<T>
as one or the other condition.
This project is my first TS experience, is this ApiResponse
type system even reasonable? It is based on an backend that I cannot change that sometimes sends Data and sometimes sends all sorts of stuff but always sends Success: boolean
at least. If you can recommend a better way to handle this type of backend response please let me know. I am also very interested to see how I can adjust this generic getApiResponse<T>()
function. Conditional types seem to be a very useful thing and I have implemented them several places in the project but I am not sure how to get over this hurdle of a generic function accepting a conditional type as the generic.