How to make typescript think resolved Promise<T> as Type T

Viewed 6683

I'm new to typescript, and I'm creating something and faced with problem that never happened when I use javascript.

Assume that we have async function what returns Promise<ResultSet>

interface ResultSet {
  status: number; // 0 for success 1 for failure
  data?: any;
}

const getDataFromServer = async (parameter: string): Promise<ResultSet> => {
   const response = await fetch(<--Prameters-->);
   return await response.json();
}

It always returns it's value as Promise.

And it need to be connected to interface so that some module can make use of this. It will change global variable's value using request above. And need to return result of action (success or failure code). And it's result of action used in lot's of place somewhere.

const storeData = async (parameter: string): number => {
  const result = await getDataFromServer(parameter);
  if (result.status == 0) {
    SOME_GLOBAL_VARIABLE.setState({ data: result.data });
  }
  return result.status;
}

As you expect, the function definition above pops an error near async (parameter: string): number cause function must returns Promise as it is async function. So it need to be below,

const storeData = async (parameter: string): Promise<number> => {
  const result = await getDataFromServer(parameter);
  if (result.status == 0) {
    SOME_GLOBAL_VARIABLE.setState({ data: result.data });
  }
  return result.status;
}

The problem begin with this. Cause return type of function is Promise, all other functions' interfaces connected with this must define that result status as Promise<number> not a number. And it will globally effected to entire server code. Also, that SOME_GLOBAL_VARIABLE's data also need to be typed as Promise<T>.

As I know, there are no way to convert promise object to normal object.

I want to cut this endless chainning of promises. Is there any way? or is it just my misunderstanding? Even replacing async ~ await to then() ~ catch() also failed, because it seems it also returns Promise.

The Promise guarantee if it's value is successfully resolve or not. But, in some point, it is already guaranteed as Resolve. But there's no way to handle this situation.

How can I deal with this situation? This seems really common case when using typescript. But i don't know how to do.

1 Answers

This is no different to normal JavaScript, it's just that TypeScript gives it a name.

The short answer is no, you cannot convert a promise of an object to the object itself. It is not because of TypeScript but rather because of the promise API (or the very nature of asynchronous code).

Once dealing with promises / async / await, the only place where you can access the promised type is within the then handler (or after using await):

// Using promises
storeData().then((result: number) => { /* result is a number here, not a promise of a number */ });

// Using async / await
const result = await storeData(); // again, result is a number, not a promise of a number

So to avoid making all your code promise based you need to call it from within the then handler (or after await):

// An example of a promise-less function somewhere in your code
const someOtherFunction = (result: number) => { /* do something with your result */ };

storeData().then(result => {
  // this works since result is a number here
  someOtherFunction(result);
});

You can of course write an abstraction that wraps a promise, an object with let's say a value property that waits for the promise and sets the value on itself once resolved. You would however still need to wait for the promise to resolve in order to get the resolution value.

Related