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

Unable to use custom Promise definition with two generic parameters as return value for async function #12973

Closed
NoelAbrahams opened this issue Dec 16, 2016 · 9 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@NoelAbrahams
Copy link

NoelAbrahams commented Dec 16, 2016

TypeScript Version: 2.1.4

Code

tsconfig.json

{
  "compilerOptions": {
    "target": "ES5",
    "module": "none",
    "noLib": true
  },
  "compileOnSave": true
}

Minimal Custom lib.d.ts

interface Object { }
interface IArguments { }
interface Function { }
interface String { }
interface Boolean { }
interface Number { }
interface Date { }
interface Array<T> { }
interface Error { }
interface RegExp { }
interface JSON { }

interface PromiseLike<T, TError> {

    then(
        onfulfilled?: ((value: T) => T | PromiseLike<T, TError>) | undefined | null,
        onrejected?: ((reason: TError) => T | PromiseLike<T, TError>) | undefined | null): PromiseLike<T, TError>;
}

interface Promise<T, TError> {

    then(onfulfilled?: ((value: T) => T | PromiseLike<T, TError>) | undefined | null, onrejected?: ((reason: TError) => T | PromiseLike<T, TError>) | undefined | null): Promise<T, TError>;
}

interface PromiseConstructor {

    readonly prototype: Promise<any, any>;

    new <T, TError>(executor: (resolve: (value?: T | PromiseLike<T, Error>) => void, reject: (reason?: any) => void) => void): Promise<T, Error>;

    reject<T>(reason: any): Promise<T, Error>;

    resolve<T>(value: T | PromiseLike<T, Error>): Promise<T, Error>;

    resolve(): Promise<void, Error>;
}

declare var Promise: PromiseConstructor;
declare type PromiseConstructorLike = new <T, TError>(executor: (resolve: (value?: T | PromiseLike<T, TError>) => void, reject: (reason?: any) => void) => void) => PromiseLike<T, TError>;

Sample Code

class Foo {


    public async foo() : Promise<number, Error> {

        return 10;
    }

    public async bar() {

        const val = await this.foo();

        return val;
    }
}

Expected behavior:

No compilation errors

Actual behavior:

The compiler prints the following errors:

error TS1057: Build:An async function or method must have a valid awaitable return type.
error TS2697: Build:An async function or method must return a 'Promise'. Make sure you have a declaration for 'Promise' or include 'ES2015' in your `--lib` option.
error TS1058: Build:Operand for 'await' does not have a valid callable 'then' member.
error TS2317: Build:Global type 'PromiseLike' must have 1 type parameter(s).
error TS2317: Build:Global type 'Promise' must have 1 type parameter(s).

I would like the rule Global type 'Promise' must have 1 type parameter(s) to be relaxed if possible.

Thanks!

Related issues:
#7588
#5413
#12737
#12292

@mhegazy
Copy link
Contributor

mhegazy commented Dec 16, 2016

I would not recommend changing the global Promise type. the compiler makes assumptions about how it is defined. same goes for PromiseLike.

if you want to use a different Promise definition, and you are running under --t es5, consider adding an explicit type annotation on you async functions with the promise type you are using, e.g.:

async function foo (a) : MyPromise<string, number, undefined> {
   .....
}

@mhegazy mhegazy added the Working as Intended The behavior described is the intended behavior; this is not a bug label Dec 16, 2016
@NoelAbrahams
Copy link
Author

@mhegazy,

the compiler makes assumptions about how it is defined. same goes for PromiseLike.

The suggestion is to relax this assumption. There is no actual change to the runtime behaviour of Promise; the only change is that the errors are strongly typed. I don't really see why the compiler should enforce this restriction.

Also, in MyPromise<string, number, undefined> how do I ensure the error is strongly typed?

@benjamingr
Copy link

This is a matter of checked vs. unchecked exceptions. You can get around this by subclassing Promise.

It's there for the same reason exceptions in regular non-promise functions aren't declared. It would make no sense to enforce this for async functions but not regular ones:

    public async foo() : number throws TypeError { // <- we don't do that in TS

        return 10;
    }

Checked exceptions have traditionally been very unpopular in languages that sport them (Java).

@NoelAbrahams
Copy link
Author

You can get around this by subclassing Promise.

how?

@gcnew
Copy link
Contributor

gcnew commented Dec 16, 2016

Checked exceptions have traditionally been very unpopular in languages that sport them (Java).

I don't think so.

@benjamingr
Copy link

@gcnew I'd like to retract that statement. Mainly because I don't have anything objective to back it up and I don't think it's the debate we should be having anyway.

@NoelAbrahams class YourPromise<T,E> extends Promise<T> and overload the signature of the static reject, and then.

@NoelAbrahams
Copy link
Author

That was something I thought would work, but the way Promise is defined in lib.d.ts it's not trivial.
(Also #12737 is a blocker, but that's not important.)

The original issue was not really about errors: it was to relax the restrictions on how Promise is defined in lib.d.ts. The fact that I'm using the second generic parameter as an error type is beside the point.

@zpdDG4gta8XKpMCd
Copy link

#12737 is fixed it says

@zpdDG4gta8XKpMCd
Copy link

related: #10751

@mhegazy mhegazy closed this as completed Apr 21, 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
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

5 participants