Reputation: 727
I've created a fair simple function to wrap around fetch. What i'm trying to figure out is how i can use TS Generics to set the Type for 'data' in the return.
const apiFetchData = async (
url: string,
options = {},
): Promise<FetchResponse> => {
try {
const res = await fetch(url, options);
const data = (await res.json()) || null;
const statusCode = res.status;
return {
data,
hasData: !!data,
statusCode,
error: (statusCode !== 200 && true) || false,
errorType: (statusCode !== 200 && 'api') || null,
};
} catch (error) {
return {
data: {
error: error.message,
},
hasData: false,
statusCode: 500,
error: true,
errorType: 'application',
};
}
};
The FetchResponse type looks like this:
type FetchResponse = {
data: {
[key: string]: {};
error?: string;
};
hasData: boolean;
statusCode: number;
error: boolean | null;
errorType?: 'api' | 'application' | null;
};
If i know my API will return the following:
type User = {
name: string;
email: string;
}
Then i somehow want my FetchResponse to be:
type FetchResponse = {
data: User;
hasData: boolean;
statusCode: number;
error: boolean | null;
errorType?: 'api' | 'application' | null;
};
That way i can then do something like this:
const response = await apiFetchData('URL');
console.log('User name is', response.data.name);
Currently i do something like this, which works, but it seems LOOOOONG
type FetchWithUserType = FetchResponse & {
data: {
error?: string;
user?: User;
};
};
and then:
const response: FetchWithUserType = await apiFetchData('URL');
console.log('User name is', response.data.name);
If generics isnt the way forward, can someone help me make this better so i can do what i'm after. Essentially the problem is that i want my VSCode intellisense to know what is within the data returned. Vs. being an and having to keep switching between code.
Upvotes: 1
Views: 2190
Reputation: 1421
It's a little unclear what you mean when you say "I know my API will return the following". I assume you don't mean your API will always return that or you would have just defined FetchResponse to include that data.
If you mean, that you know the API will return the structure for a certain URL, you could handle that with generics as follows:
async function apiFetchData<T extends {[key:string]: any}>(
// function params omitted
):Promise<FetchResponse<T>> => {
// function code omitted
}
And then define FetchResponse as
type FetchResponse<S> = {
data: <S>
// rest of the type
}
Basically you are using the generic to tell TS that you know what the form of the data will be so it can insert it into the FetchResponse type.
If you mean that you don't know what the API will return in advance, but it might return data in the format User, then you will need to use a type guard. But I think you probably mean something like the previous example.
Upvotes: 2