Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

awaited operator doesn't work with type guards #37512

Closed
falsandtru opened this issue Mar 21, 2020 · 6 comments · Fixed by #37610
Closed

awaited operator doesn't work with type guards #37512

falsandtru opened this issue Mar 21, 2020 · 6 comments · Fixed by #37610
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript

Comments

@falsandtru
Copy link
Contributor

@rbuckton

TypeScript Version: master

Search Terms:

Code

declare function isAwaited(value: any): value is awaited unknown;
function f<a>(a: a): void {
  isAwaited(a) && a;
}

Expected behavior:
The narrowed type of a is awaited a.
Actual behavior:
The narrowed type of a is a.

Playground Link:

Related Issues:

@falsandtru falsandtru changed the title awaited predicate doesn't work with type guards awaited operator doesn't work with type guards Mar 21, 2020
@RyanCavanaugh RyanCavanaugh added In Discussion Not yet reached consensus Suggestion An idea for TypeScript labels Mar 22, 2020
@falsandtru
Copy link
Contributor Author

Probably we have to replace isPromiseLike with isAwaited.

          if (isPromiseLike(value)) {
            internal.status = {
              state: State.resolved,
              result: value,
            };
            value.then(
              value => {
                assert(internal.status.state === State.resolved);
                internal.status = {
                  state: State.fulfilled,
                  result: value,
                };
                resume(internal);
              },
              reason => {
                assert(internal.status.state === State.resolved);
                internal.status = {
                  state: State.rejected,
                  result: reason,
                };
                resume(internal);
              });
          }
          else {
            internal.status = {
              state: State.fulfilled,
              result: value!, // error
            };
            resume(internal);
          }
Type 'NonNullable<T>' is not assignable to type 'awaited T'.
  Type 'T' is not assignable to type 'awaited T'.ts(2322)

https://github.com/falsandtru/spica/blob/v0.0.343/src/promise.ts

@rbuckton
Copy link
Member

The problem is that awaited unknown is just reduced to unknown, so you are asserting that value is unknown. The awaited type does work in type guards:

image

Playground link: https://www.typescriptlang.org/play/index.html?ts=3.9.0-dev.20200322#code/CYUwxgNghgTiAEAzArgOzAFwJYHtXywGcBBAdyiwxGAB4AVAPgAoA3KCZEALnjvgB94UcpWq8AlDzYcERISKrBeAbgBQKdNjxJ6zAB48+g4RUUT4Ab1XwbBRPCZEyp6kz3jxl67Z961PgF9VAKA

@rbuckton
Copy link
Member

Let me know if that addresses your concerns so that I can close this issue.

@falsandtru
Copy link
Contributor Author

falsandtru commented Mar 24, 2020

Basically type arguments are not used in type guards. Can you remove type arguments to accept all types? Actually, your function doesn't accept T | awaited T | PromiseLike<T>.

Ideally, since T can be considered as thenable when T is not awaited T, isAwaited function should work as follows. Here isAwaited function proves or ensures that T is just a union of awaited T and PromiseLike<T>.

declare function isAwaited(value: any): value is awaited any; // or awaited typeof value
function f<T>(x: T | awaited T | PromiseLike<T>) {
    if (isAwaited(x)) {
        x; // awaited T
    }
    else {
        x; // PromiseLike<T>
    }
}

@nmain
Copy link

nmain commented Mar 24, 2020

Just to make sure I'm understanding this right, would this be a reasonable implementation of isAwaited? No matter what T is, awaited T is never PromiseLike ("thennable"), so value is awaited unknown just means that value is not PromiseLike.

function isAwaited(value: any): value is awaited unknown {
    return !value || typeof value.then !== "function";
}

@rbuckton
Copy link
Member

awaited is only preserved when the type argument is generic, so awaited unknown is just unknown. Similar to awaited any is just any. It's only preserved at higher order.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants