API endpoints that I call deliver data in the following shapes:
interface ArrayResponse<T> {
data: Partial<T>[]
}
interface ObjectResponse<T> {
data: Partial<T>
}
I've written a function that calls endpoints and returns responses:
function fetchOne<T>(path: string): ObjectResponse<T> {
// HTTP call
}
function fetchMany<T>(path: string): ArrayResponse<T> {
// Exact same HTTP logic as above
}
Ideally I want to have a single response type:
interface Response<T> {
data: Partial<T> | Partial<T>[]
}
But I what I really want is to be able to have the above type to determine Partial or Partial[] based on the value passed for T.
So Response<T> would result in data: Partial<T> and Response<T>[] results in data: Partial<T>[].
I'm also open to other creative ways of achieving the same effect.
It can be done. But regardless of how you set this up you would need to manually set the generic T when calling, like fetch<MyType>(path), in order to get a meaningful response type because the generic T cannot possibly be inferred from the path which is a string.
Try this:
function fetchEither<T>(path: string): T extends (infer U)[] ? ArrayResponse<U> : ObjectResponse<T> {
}
With this setup, you would pass in MyType as T to get a single response or MyType[] as T for a multiple response. (note: this assumes that MyType itself would not be an array)
const array = fetchEither<MyType[]>(""); // has type ArrayResponse<MyType>
const object = fetchEither<MyType>(""); // has type ObjectResponse<MyType>
Typescript Playground Link
Given that T cannot be inferred, it might make more sense to just return a union type and determine the value at runtime as proposed by @Thatkookooguy.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With