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

let promises be unions #10751

Closed
zpdDG4gta8XKpMCd opened this issue Sep 7, 2016 · 8 comments
Closed

let promises be unions #10751

zpdDG4gta8XKpMCd opened this issue Sep 7, 2016 · 8 comments
Labels
Out of Scope This idea sits outside of the TypeScript language design constraints Suggestion An idea for TypeScript

Comments

@zpdDG4gta8XKpMCd
Copy link

zpdDG4gta8XKpMCd commented Sep 7, 2016

nightly build, September 7, 2016

currently in order for async/await to work it has to be a solid interface with a then method, await doesn't accept a union that has a callable then, please allow unions for promises

// tsconfig.json
{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es6",
        "noLib": true
    }
}
const enum PromisedKind { Resolved, Unresolved }

interface Thenable<a> {
    then<b>(onfulfilled?: (value: a) => b | Promise<b>, onrejected?: (reason: any) => b | Promise<b>): Promise<b>;
    then<b>(onfulfilled?: (value: a) => b | Promise<b>, onrejected?: (reason: any) => void): Promise<b>;
}

type Promise<a> = Resolved<a> | Unresolved<a>;

interface Resolved<a> extends Thenable<a> {
    kind: PromisedKind;
    value: a;
}

interface Unresolved<a> extends Thenable<a> {
    kind: PromisedKind;
    thens: { (value: a): void; }[];
}

interface Array<a> {}
interface Boolean {}
interface Function {}
interface Number {}
interface Object {}
interface RegExp {}
interface String {}
interface IArguments {}
var String: StringConstructor;
interface StringConstructor {
  new (value?: any): String;
  (value?: any): string;
  prototype: String;
  fromCharCode(...codes: number[]): string;
}


declare const value: Promise<number>;

async function test() {
    void value.then(x => { }); // <-- then exists and is callable
    return await value; // Operand for 'await' does not have a valid callable 'then' member.
}

image

@jods4
Copy link

jods4 commented Sep 8, 2016

Actually, you can await anything, not only promises.
It should behave as if you did await Promise.resolve(x) (note that if x was a promise already, Promise.resolve(x) should return x itself).

EDIT: well, that's the specified JS behavior, of course. Now the question is: in a typed world, does it make sense or do we want to be more strict and await Promises only (in the same way TS refuses to do "x" * 2 when it's runnable JS)?
I would argue that it is sometimes useful to do as @Aleksey-Bykov showed and mix sync and async implementations. I personnally do that in a rule engine I wrote. I don't think it is dangerous from a typing point of view.

EDIT 2: thinking some more... maybe it should raise a warning if you await something that cannot be a Promise/Thenable?
So going back to OP: let a: X|Y = await Promise<X>|Y is ok and works, let a: Y = await Y actually works but is dubious and should be flagged as an error?

@aluanhaddad
Copy link
Contributor

well, that's the specified JS behavior, of course

@jods4 I am not sure I understand this statement. JavaScript does not have interfaces.

@jods4
Copy link

jods4 commented Sep 9, 2016

@aluanhaddad I meant that JS supports awaiting any value, not only promises/thenable. It is not an error to await 4.

@Arnavion
Copy link
Contributor

Arnavion commented Sep 9, 2016

maybe it should raise a warning if you await something that cannot be a Promise/Thenable?

#8310

@jods4
Copy link

jods4 commented Sep 9, 2016

@Arnavion Thanks for the link. I see this was beaten to death in several issues already. So I guess we should be able to await anything!

I noticed @RyanCavanaugh said in that thread:

We clearly want to allow await T | Promise<T>

But as @Aleksey-Bykov says, the following code trips up the type checker:

let x: number | { then(): number };
async function f() {    
    return await x;  // Error
}

I think that TS should distribute the await operation and deduce that the result is number | number, well number.

@aluanhaddad
Copy link
Contributor

@jods sorry, it appears I misunderstood the context. Distributing the await is a nice idea but it seems to suggest different runtime semantics as you have already pointed out. That would introduce type based runtime behavior...

@jods4
Copy link

jods4 commented Sep 10, 2016

@aluanhaddad

it seems to suggest different runtime semantics as you have already pointed out

Not sure what you are referring to.

By 'distributing' I meant that the expression await (X | Promise<Y> | Thenable<Z>) should be valid and have type X | Y | Z.

@mhegazy mhegazy added Suggestion An idea for TypeScript Out of Scope This idea sits outside of the TypeScript language design constraints labels Apr 27, 2017
@mhegazy
Copy link
Contributor

mhegazy commented Apr 27, 2017

Given that Promises are built-in type and well defined in the spec. changing what it means seems like an advanced option, and would be out of scope for the time being.

@mhegazy mhegazy closed this as completed Apr 27, 2017
@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Out of Scope This idea sits outside of the TypeScript language design constraints Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

5 participants