Skip to content

Promises of different types should not be compatible #9953

Description

TypeScript Version: nightly

Code

const x: Promise<string> = Promise.resolve("");
const y: Promise<number> = x;

Expected behavior:
Compiler error on line 2.

Actual behavior:
No error.

Activity

  1. yortus commented on Jul 27, 2016

    @yortus
    Contributor

    In the definition of Promise<T> in lib.d.ts, if you comment out the nullary overload of then, then you get the expected behaviour. I do not understand why, I was just trying to reproduce the behaviour with a simpler case and discovered that.

    // in lib.d.ts:
    interface Promise<T> {
       /*...*/
        then<TResult1, TResult2>(onfulfilled: (value: T) => TResult1 | PromiseLike<TResult1>, onrejected: (reason: any) => TResult2 | PromiseLike<TResult2>): Promise<TResult1 | TResult2>;
       /*...*/
        // then(): Promise<T>;     <===== comment this one out
       /*...*/
    }
    
    
    // in code.ts:
    const x: Promise<string> = Promise.resolve("");
    const y: Promise<number> = x; // ERROR: 'Promise<string> is not assignable to 'Promise<number>'
    
    
  2. jeffreymorlan commented on Jul 27, 2016

    @jeffreymorlan
    Contributor

    I came up with those overloads in #9193, but under closer examination they are actually subtly wrong. TypeScript's model of function arguments assumes that extra args beyond those declared in the signature are ignored - and therefore, a function signature is assignable to a function signature with more parameters. Although you can pass 0 or 1 arguments to Promise.then, it does not ignore its first and second arguments; we do not want this rule applying to it.

    I think the right way to declare Promise.then is to make all the overloads 2-arg, with optional parameters of type null (technically, any non-function can be used to get the passthrough behavior, but passing anything other than null/undefined is almost certainly a mistake)

    interface Promise<T> {
        then(onfulfilled?: null, onrejected?: null): Promise<T>;
        then<TResult>(onfulfilled: (value: T) => TResult | PromiseLike<TResult>, onrejected?: null): Promise<TResult>;
        // other overloads here
    }
    

    EDIT: This only fixes the problem when --strictNullChecks is on. Changing null to never doesn't help, but changing it to string does. Not sure why...

  3. OliverJAsh commented on Sep 23, 2016

    @OliverJAsh
    Contributor

    Is this still an issue?

    const commits: Promise<number> = Promise.resolve('foo') // no error!
  4. mhegazy commented on Sep 29, 2016

    @mhegazy
    Contributor

    thanks Oliver Joseph Ash (@OliverJAsh). this should be fixed after Ron Buckton (@rbuckton)'s latest change to the Promise definitions.

  5. ghost reopened this on Oct 14, 2016
  6. modified the milestones: TypeScript 2.1, on Oct 27, 2016
  7. ghost closed this as completedon Apr 19, 2017
  8. locked and limited conversation to collaborators on Jun 19, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    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