Reputation: 14863
I have the following code that tries to do a AJAX-request, then decode the payload while maintaining types all the way through:
export function fetchJson<T>(url: string, data: FetchDataTypes): Promise<T> {
return new Promise((resolve, reject) => {
fetch(url, {})
.then((response: Response) => {
if (!response.ok) {
reject(response);
} else {
return response.text() as Promise<string>;
}
})
.then((text: string) => {
try {
resolve(JSON.parse(text) as T);
} catch (err) {
reject(err);
}
})
.catch((err: Promise<void>) => reject(err));
});
}
The problem is that eslint throws this error:
Expected to return a value at the end of arrow function consistent-return
I have looked at the documentation, and I am pretty sure it is because the first .then
callback has an inconsistency in return/no-returns. I have tried to mutate my code to either have returns or not, but I can not for the life of me figure out how to rewrite this code to make eslint happy again.
All related questions I could find were errors where the developer forgot to return from mapper methods etc, and I could not find them relevant to my scenario.
Edit: Just to be clear, I want to be able to call my function like this:
export const fetchSomeObject = (): Promise<SomeResponseType> =>
fetchJson('/api/some-endpoint', {
method: 'GET',
headers: {},
});
And then:
fetchSomeObject.then((response: SomeResponseType) => {
[...]
});
Upvotes: 1
Views: 946
Reputation: 371019
While you could fix it by always return
ing and tweaking the .then
logic, it would be far better to avoid the explicit Promise construction antipattern entirely, sidestepping the issue completely. When there's an error in a Promise, you can throw
inside it to stop the handler and make control flow go to the catch
.
You should catch
in the consumer of fetchJson
- don't catch
here, because then, when the fetch fails, you'll be returning something of the wrong type - when you catch
, you return a resolved Promise, which is not desirable here.
If you want to JSON.parse
a response, use the .json()
method, not the .text()
method, and it'll be done automatically.
Also note that there's no need to explicitly note the types when TS can already infer it.
export function fetchJson<T>(url: string): Promise<T> {
return fetch(url)
.then((response) => {
if (!response.ok) {
throw new Error(response.statusText);
} else {
return response.json();
}
});
}
// Then in the consumer:
fetchJson<someType>('someURL')
.then((result) => {
// result is of type someType
})
.catch((err) => {
// handle errors here
});
Upvotes: 2