Search code examples
javascripttypescriptassert

TS: Assert function return based on Promise result


PROBLEM

Given a function returning a Promise,

const fetchSomething = async (): Promise<DocumentType | ServerErrorType> => {
  const data = await fetch(`/data`, {
    method: "get",
    headers: { "Content-Type": "application/json" },
  });
  try {
    const res = await data.json();
    return Promise.resolve(res);
  } catch (err) {
    return Promise.reject(err);
  }
};

Called as such,

// TS evaluates both res and err as 'DocumentType | ServerErrorType'
fetchSomething()
  .then((res) => console.log(res))
  .catch((err) => console.log(err))

How can it be asserted and called in a way that if the Promise resolves, the result will be asserted as a DocumentType, while if it rejects, then it asserts the result as a ServerErrorType?

ALTERNATIVE SOLUTION

Quick and easy solution, is to assert the value directly when calling the function, as followed:

// TS evaluates res as 'DocumentType' and err as 'ServerErrorType'
fetchSomething()
  .then((res as DocumentType) => console.log(res))
  .catch((err as ServerErrorType) => console.log(err))

The problem in asserting as such, is I would end up asserting the results of the function anywhere I call it.

WHAT I TRIED

I tried asserting the values on fetchSomething this way:

try {
  const res = await data.json();
  return Promise.resolve(res as DocumentType);
} catch (err) {
  return Promise.reject(err as ServerErrorType);
}

But that doesn't seem to help TS evaluating the correct type.

CONCLUSIONS

Is there a clean way of asserting the returned values directly inside that function, based on the Promise result (either resolved or rejected)?


Solution

  • Type annotations on catch variable is currently not supported in Typescript. See here.

    BTW I do recommend changes suggested by @T.J.Crowder