OptimusCrime
OptimusCrime

Reputation: 14863

Decoding JSON with fetch and TypeScript throws eslint "Expected to return a value at the end of arrow function" error

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

Answers (1)

CertainPerformance
CertainPerformance

Reputation: 371019

While you could fix it by always returning 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

Related Questions