0
votes

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.

2
This seems to be a misunderstanding of handling async functionality. How you handle the usage fetch and subsequent callbacks doesn't change from JavaScript to TypeScript. In both JavaScript and TypeScript you'll still need to either await the Promise or use a then callback. You shouldn't need to change the typing of SOME_GLOBAL_VARIABLE to Promise<T> because that's after the async part, therefore the data is already resolved.Paul

2 Answers

4
votes

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.

1
votes

Tonight I ate burgers. We had no buns, so my wife went to buy some. I could not assemble the burgers until my wife returned from the shop with buns. Accordingly, I also had to wait to eat the buns, to wash up my plate, etc.

I hope that example is not too bizarre; chains of asynchronous dependencies seem rather straight forward in 'real-life', and whilst this is not at all a direct analogy, I hope it goes some way to demonstrating why typescript is giving the types which it is.

As @ján-jakub-naništa's answer points out, the situation is exactly the same in javascript as it is in typescript; it's just that typescript makes it a little more obvious what is happening.

There are many ways out of this chaining of async functions, but ultimately you will still need to wait for the value. The best I can suggest is to try and isolate your asynchronous code as much as possible; if you have a Promise<number> for instance, have the resolution of that promise call a function which just takes a number. This way you can avoid your code becoming 'tainted' with unnecessary Promise types.