Typescript: Typescript fails to throw error when a generic is assigned to another generic of whom it's generic type is a superset

Created on 9 Feb 2017  Β·  9Comments  Β·  Source: microsoft/TypeScript

TypeScript Version: 2.1.4

Code

// A *self-contained* demonstration of the problem follows...
export interface MyInterface {
  myFunction(): Promise<number | string>;
}

export class MyClass implements MyInterface {
  async myFunction() { // inferred return type Promise<number | string | string[]>
    let num = 1;
    let text = 'text';
    if (num) {
      return num;
    }
    if (text) {
      return text;
    }
    return [text];
  }
}

Expected behavior:
TS throws an error that MyClass incorrectly implements MyInterface
type Promise<number | string | string[]> not assignable to Promise<number | string>

otherwise a client using the class as MyInterface can call the method and only handle a return promise of string or number and have errors when the promise is resolved with the array.

Actual behavior:
No error is thrown. (It's worth noting this doesn't repro without the generic. If for example the method were not async.) This also is not thrown if the return type is manually specified as Promise<number | string | string[]> just to be clear inference is not to blame.

Canonical

Most helpful comment

@DanielRosenwasser ok :) i don't fully understand the terms you used but i think i get the gist. glad to hear it's being worked on :)

Sorry about that - that was a bit more jargon-y than I realized. πŸ˜„

image

All 9 comments

here's an even simpler example that doesn't require classes or interfaces

const myPromise: Promise<string> = 
  new Promise<string | string[]>((resolve) => { resolve(['anything']) })

myPromise.then(value => {
  value.substr(0, 3); //throws an error because arrays lack this function
});

and using inference rather than explicit same thing:

 const myPromise: Promise<string> = new Promise((resolve) => {
    let text = '';
    if (text) {
      resolve(text);
    }
    resolve([text]);
  });

  myPromise.then(value => {
    value.substr(0, 3); //throws an error because arrays lack this function
  });

code example without the generic to prove that normally a superset is unassignable

function myFunction() { // inferred return 'number | string | string[]'
  let num = 1;
  let text = 'text';
  if (num) {
    return num;
  }
  if (text) {
    return text;
  }
  return [text];
}
const myValue: string | number = myFunction(); //ts throws unassignable error

@gmoothart @evmar @bb @joshk @huerlisi sorry to ping you all. not sure who should take a look at this. seems like a pretty major bug.

This is actually a result of structural compatibility and function parameter bivariance. Essentially when Promise#then is compared between the two, the parameter type of arguments are compared in a bivariant way. Since the contravariant side succeeds, the two are compatible in either direction.

This is undesirable, but we're thinking about fixes. See this section in our FAQ for more details.

@DanielRosenwasser ok :) i don't fully understand the terms you used but i think i get the gist. glad to hear it's being worked on :)

The correct error should be reported with https://github.com/Microsoft/TypeScript/pull/15104.

awesome!

On Thu, 18 May 2017 at 09:53 Mohamed Hegazy notifications@github.com
wrote:

The correct error should be reported with #15104
https://github.com/Microsoft/TypeScript/pull/15104.

β€”
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
https://github.com/Microsoft/TypeScript/issues/13970#issuecomment-302469238,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABE5mDzTMaRcBXRA2EtEyOC78AXJCYAXks5r7HdsgaJpZM4L72sH
.

@DanielRosenwasser ok :) i don't fully understand the terms you used but i think i get the gist. glad to hear it's being worked on :)

Sorry about that - that was a bit more jargon-y than I realized. πŸ˜„

image

hahahaha! best. thanks again for the fix :)

On Thu, 18 May 2017 at 12:50 Daniel Rosenwasser notifications@github.com
wrote:

@DanielRosenwasser https://github.com/danielrosenwasser ok :) i don't
fully understand the terms you used but i think i get the gist. glad to
hear it's being worked on :)

Sorry about that - that was a bit more jargon-y than I realized. πŸ˜„

[image: image]
https://cloud.githubusercontent.com/assets/972891/26220693/7999d318-3bc8-11e7-9f79-44ad256789a9.png

β€”
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
https://github.com/Microsoft/TypeScript/issues/13970#issuecomment-302522148,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABE5mPC3p7xJ_KZahOLNexga_aW57F64ks5r7KD_gaJpZM4L72sH
.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

fwanicka picture fwanicka  Β·  3Comments

dlaberge picture dlaberge  Β·  3Comments

wmaurer picture wmaurer  Β·  3Comments

MartynasZilinskas picture MartynasZilinskas  Β·  3Comments

Zlatkovsky picture Zlatkovsky  Β·  3Comments