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

Incorrect inference of type when using promise.then(...) #13117

Closed
awalgarg opened this issue Dec 22, 2016 · 0 comments · Fixed by #13487
Closed

Incorrect inference of type when using promise.then(...) #13117

awalgarg opened this issue Dec 22, 2016 · 0 comments · Fixed by #13487
Assignees
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue

Comments

@awalgarg
Copy link

awalgarg commented Dec 22, 2016

TypeScript Version: 2.1.1 / nightly (2.2.0-dev.201xxxxx)

Code

interface A { m(): Promise<any> }
interface B { m(): Promise<number> }
function a(o: A): Promise<number> {
    return Promise.resolve()
        .then(_ => o.m());
}
function adash(o: A): Promise<number> {
    return o.m();
}
function b(o: B): Promise<number> {
    return Promise.resolve()
        .then(_ => o.m());
}

Expected behavior:
Should compile.

Actual behavior:
function a doesn't compile. The return type of the return statement is inferred as Promise<void>, while it should be inferred as Promise<any>, which is compatible with the intended return type (Promise<number>) of the parent function (a), just like type is correctly inferred as Promise<number> in the function b.

The offending part here is the library definitions for the Promise interface, at https://github.com/Microsoft/TypeScript/blob/master/lib/lib.es2015.promise.d.ts#L31. The linked definition of then is matched on the result of Promise.resolve(), which is a Promise<void>, and the return type is inferred as Promise<void> only. Apparently order matters, and the correct matching for that expression should be https://github.com/Microsoft/TypeScript/blob/master/lib/lib.es2015.promise.d.ts#L47 instead, so the return type is inferred as Promise<any> instead.

Simply re-ordering those definitions of .then solves the issue. But I am not sure what the further implications are.

diff --git a/src/lib/es2015.promise.d.ts b/src/lib/es2015.promise.d.ts
index e27da2c..63fef0f 100644
--- a/src/lib/es2015.promise.d.ts
+++ b/src/lib/es2015.promise.d.ts
@@ -8,7 +8,7 @@ interface Promise<T> {
      * @param onrejected The callback to execute when the Promise is rejected.
      * @returns A Promise for the completion of which ever callback is executed.
      */
-    then(onfulfilled?: ((value: T) => T | PromiseLike<T>) | undefined | null, onrejected?: ((reason: any) => T | PromiseLike<T>) | undefined | null): Promise<T>;
+    then<TResult>(onfulfilled: ((value: T) => T | PromiseLike<T>) | undefined | null, onrejected: (reason: any) => TResult | PromiseLike<TResult>): Promise<T | TResult>;
 
     /**
      * Attaches callbacks for the resolution and/or rejection of the Promise.
@@ -16,7 +16,7 @@ interface Promise<T> {
      * @param onrejected The callback to execute when the Promise is rejected.
      * @returns A Promise for the completion of which ever callback is executed.
      */
-    then<TResult>(onfulfilled: ((value: T) => T | PromiseLike<T>) | undefined | null, onrejected: (reason: any) => TResult | PromiseLike<TResult>): Promise<T | TResult>;
+    then<TResult>(onfulfilled: (value: T) => TResult | PromiseLike<TResult>, onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<TResult>;
 
     /**
      * Attaches callbacks for the resolution and/or rejection of the Promise.
@@ -24,7 +24,7 @@ interface Promise<T> {
      * @param onrejected The callback to execute when the Promise is rejected.
      * @returns A Promise for the completion of which ever callback is executed.
      */
-    then<TResult>(onfulfilled: (value: T) => TResult | PromiseLike<TResult>, onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<TResult>;
+    then<TResult1, TResult2>(onfulfilled: (value: T) => TResult1 | PromiseLike<TResult1>, onrejected: (reason: any) => TResult2 | PromiseLike<TResult2>): Promise<TResult1 | TResult2>;
 
     /**
      * Attaches callbacks for the resolution and/or rejection of the Promise.
@@ -32,7 +32,7 @@ interface Promise<T> {
      * @param onrejected The callback to execute when the Promise is rejected.
      * @returns A Promise for the completion of which ever callback is executed.
      */
-    then<TResult1, TResult2>(onfulfilled: (value: T) => TResult1 | PromiseLike<TResult1>, onrejected: (reason: any) => TResult2 | PromiseLike<TResult2>): Promise<TResult1 | TResult2>;
+    then(onfulfilled?: ((value: T) => T | PromiseLike<T>) | undefined | null, onrejected?: ((reason: any) => T | PromiseLike<T>) | undefined | null): Promise<T>;
 
     /**
      * Attaches a callback for only the rejection of the Promise.
@@ -251,4 +251,4 @@ interface PromiseConstructor {
     resolve(): Promise<void>;
 }
@mhegazy mhegazy added this to the TypeScript 2.2 milestone Dec 22, 2016
@mhegazy mhegazy added the Bug A bug in TypeScript label Dec 22, 2016
@mhegazy mhegazy added the Fixed A PR has been merged for this issue label Feb 15, 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
Bug A bug in TypeScript Fixed A PR has been merged for this issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants