question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Unsound `await` handling

See original GitHub issue
async function foo<T>(val: T) {
  const v = await val;
  return v;
}

async function test() {
  const promise = Promise.resolve(1);
  const val = await foo(promise);
  const a = val.then(); // Runtime error
}

test();

I assume that await and similar promise methods have signature like this:

await<T>(Promise<T> | T) => T;

This is incorrect. If type of T is not known to be a promise (i.e. unknown or generic T), it is assumed NOT to be a promise, while at runtime it could be a promise.

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:1
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
vkurchatkincommented, Jun 11, 2020

Not really fixed though. A tiny modification and you get the same thing:

async function foo<T>(val: T): Promise<T> {
  const v = await val;
  return v;
}

async function test() {
  const promise: Promise<1> | string = Promise.resolve(1);
  const val = await foo(promise);

  if (typeof val !== 'string') {
    const a = val.then(); // Runtime error
  }
}

test();
0reactions
vkurchatkincommented, Jun 12, 2020

Let’s say there is a generic type for that called $Await<T> which is supposed to evaluate to the type of expression await x where x has type T.

T can be one of the following

Thenable<T>

interface Thenable<T> {
  then<T>(fn: (val: T) => unknown): unknown;
}

For thenables, $Await<Thenable<T>> equals $Await<T>.

False-thenable

False-thenable is an object, for which obj.then evaluates to a function, but it’s not actually a thenable. Applying $Await to a false-thenable makes no sense, since there is no way to evaluate it.

Non-thenable

For everything else $Await<T> equals T;

So here is a possible way to deal with that. await x (or Promise.resolve(x) or other) evaluates to type $Await<T>. Type $Await<T> can be further refined if we have additional information about T. If T is known to be a thenable, we evaluate $Await<Thenable<T>> to $Await<T> recursively. If T is known to be a non-thenable, $Await<T> evaluate to T. Otherwise $Await<T> should stay opaque, since there is no way to tell what it is.

Additionally, it should be illegal to await on values, that could possibly be false-thenables. Here is an example:

async function test(x: unknown) {
  await x; // This should not be allowed
}


const obj = {
  then(str: string) {
    return str.toLowerCase(); // runtime error
  }
}

test(obj);
Read more comments on GitHub >

github_iconTop Results From Across the Web

Asynchronous programming: futures, async, await | Dart
Asynchronous operations let your program complete work while waiting for another operation to finish. Here are some common asynchronous operations: Fetching ...
Read more >
Error handling with Async/Await in JS | by Ian Segers | ITNEXT
Now we have the classic problem, thisThrows returns a rejecting promise, so the regular try...catch is not able to catch the error. As...
Read more >
Null safe async returns are currently unsound #944 - GitHub
The following program results in a variable of type int containing null (as implemented on the VM, and I believe as currently specified): ......
Read more >
Scoped threads in Rust, and why its async counterpart ... - Wisha
Unfortunately, scoped_tasks as envisioned provides an unsound API. How so? Let's demonstrate by abusing the function step-by-step. First, we ...
Read more >
Blog post: scoped threads in Rust, and why its async ... - Reddit
Resource leaks in rust (whether memory, a future, a file handle, whatever) are categorically not unsound and not unsafe. When writing safe code, ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found