Skip to content

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

Closed
@awalgarg

Description

@awalgarg

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>;
 }

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScriptFixedA PR has been merged for this issue

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions