Typescript: "'await' has no effect on the type of this expression", setState in react

Created on 16 Oct 2019  路  24Comments  路  Source: microsoft/TypeScript



TypeScript Version: 3.2.2

Code
await this.setState({ someState: true })

Expected behavior:
Since one can await setState, I expected no warning, this warning is actually incorrect.

Actual behavior:
image

Working as Intended

Most helpful comment

You probably want

await new Promise(accept => this.setState({}, accept));

All 24 comments

does this.state return a value?

If not it probably won't do anything. Could you send a snippet of your component?

Sorry, I thought it was enough with the info in the original post, but here's a dummy component I quickly set up, it happens here too :)

interface IProps extends RouteComponentProps { }

interface IState {
  hello: string;
}

export class MyComponent extends Component<IProps, IState> {
  constructor(props: IProps) {
    super(props);
    this.state = {
      hello: ''
    }
  }

  async componentDidMount() {
    await this.setState({hello: 'hello world'})
  }

  render() {
    return <p>{this.state.hello}</p>
  }
}

image

setState() returns void. It does not return a Promise<T>, so awaiting makes no sense.

Besides the above, I can not reproduce this warning in the playground. Is it by chance a warning of es/tslint, and not TypeScript? (I might just be out of loop and the playground does not support this well.)

You also might want to update your TypeScript version, 3.2.2 is quite old (currently 3.6.4).

Ah, right, I'll try updating.
Also, I know it's counter-intuitive to await a void... but awaiting actually does happen.
Say you run two different setState calls right after each other, only one will actually get called (if memory serves me right it's the last one), that is, unless the first one is awaited.

https://stackoverflow.com/questions/47019199/why-does-async-await-work-with-react-setstate

It's not eslint as I don't have that in the project, I do have TS-lint, but after disabling it, the warning is still there, so I assume it's from typescript

Suggestions are just suggestions; you're free to ignore them if you disagree with them. They won't appear as build errors or anything.

IMHO this code is a smell, because you could have written as

this.setState({ });
await undefined;

to make it clear to the next reader of your code that you're using await to trigger an event loop tick, not waiting for setState's Promise to do something.

You probably want

await new Promise(accept => this.setState({}, accept));

Although not directly related to the Typescript issue at hand, it's worth mentioning that while what you're doing is completely legal Javascript, and completely legal Typescript, and will accomplish exactly what you want given how React is coded right now, it's not something that's guaranteed as a part of React's API contract, and future versions of React might not finish all processing before the next turn of the event loop. @ilogico 's example is the way that's guaranteed to work with React.

This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

same issue here, i can't resolve

It isn't an issue... It is working as intended.

Hey @kitsonk I don't think this warning is entirely accurate though. As with my example here https://codesandbox.io/s/await-ing-void-in-ts-6cptn

const doSomething = async (value: string) => {
  await new Promise(res => setTimeout(res, 3000));
};

const doTheAsyncAwait = async () => {
  console.log("start doTheAsyncAwait..");
  await doSomething("cool");
  console.log("finish doTheAsyncAwait!");
};

const dontDoAsyncAwait = () => {
  console.log("start dontDoAsyncAwait..");
  doSomething("cool");
  console.log("finish dontDoAsyncAwait!");
};

doTheAsyncAwait();
dontDoAsyncAwait();

In this example, the await in doTheAsyncAwait fn waits for the Promise inside doSomething to resolve (waits for 3 seconds). Even though it's returning void, clearly the await _has an effect_ on it.

Not sure if I should file this as a separate issue since this isn't in particular about setState. But I ran into this warning when I was making a form and I'm letting the parent component in React handle the async call to the DB while the form component is waiting for the operation to finish so it can update the UI, but it doesn't really need any return value, hence the void.

@ninoM At no point in your example are you awaiting anything that's not a Promise, and accordingly the typescript compiler does not generate any warnings on that code.

@nmain in the codesandbox link, no warnings are shown. Try it in VS Code though, and you'll get the same warning message and code ts(80007) as OP.

@TheLogan has already created an issue of this in VS code's repo and they said that the issue should be filed in there.

vscode showing errors as @ninoM says. The vscode repo is pointing the finger at typescript though, and directing issues to be raised here:
https://github.com/microsoft/vscode/issues/80853#issuecomment-531323539

@ninoM Doesn't reproduce for me in VSCode either. You'll have to try harder creating a reproducing test case, and file it as a new issue if you do because the original issue here is about await when combined with React's setState, not anything else.

@JDtheGeek If you have a reproducible situation where you think this error message is being raised incorrectly, please post a new issue for it.

I found the solution. This suggestion pops up because you have put a wrong object after await. You can completely get rid of this by putting a promise (without brackets) or a function that returns a promise after the await keyword.

It happens If you use wrong jsdoc return type, for example:

// js

/**
 * @return {Array<Object>}
 */
async function fetchPosts() {
   return await axios.get('/posts');
}

async function fetchData() {
  const posts = await fetchPosts();
}

In the fetchPosts jsdoc, you should use {Promise<Array<Object>>}.

What towry said was what fixed the issue for me

Can we disable these messages?

I get this message with

await void 0

but the expression _does_ have an effect: it defers the code after it to the next microtask, which is what one may intend (that's what I use it for, when needed).

The following are similar too:

await undefined
await null
await 0

It can be useful. This shows how it works:

async function foo() {
  console.log('foo before')
  await 0
  console.log('foo after')
}

foo()

console.log('test')

output:

foo before
test
foo after

Same issue with this JS code using VS Code:

const s = require("sqlite3");
const util = require("util")
const base = new s.Database('./example.db');
base.each = util.promisify(base.each);
(async () => {
    let example = await base.each("select * from Example;"); // The suggestion appears here
    console.log(example);
})();

Removing await makes example equal Promise { <pending> }.

Same issue with this JS code using VS Code:

const s = require("sqlite3");
const util = require("util")
const base = new s.Database('./example.db');
base.each = util.promisify(base.each);
(async () => {
    let example = await base.each("select * from Example;"); // The suggestion appears here
    console.log(example);
})();

Removing await makes example equal Promise { <pending> }.

Try let example = await Promise.resolve(base.each("select ...")).

Try let example = await Promise.resolve(base.each("select ...")).

But what's the actual difference between the two?

Try let example = await Promise.resolve(base.each("select ...")).

But what's the actual difference between the two?

Maybe the each function has jsdoc that returns something other than a Promise or vscode can not get enough type information about the each function.

Wrap it in a resolved promise explicitly tell the vscode that you are await a promise, so you can use await without "annoying suggestion"?

That makes sense. I think I'll just deal with the suggestion to keep the code clean.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

weswigham picture weswigham  路  3Comments

siddjain picture siddjain  路  3Comments

seanzer picture seanzer  路  3Comments

DanielRosenwasser picture DanielRosenwasser  路  3Comments

blendsdk picture blendsdk  路  3Comments