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

3.5.1 breaks simple unambiguous type guards? #31853

Closed
ziriax opened this issue Jun 11, 2019 · 11 comments
Closed

3.5.1 breaks simple unambiguous type guards? #31853

ziriax opened this issue Jun 11, 2019 · 11 comments
Labels
Needs More Info The issue still hasn't been fully clarified

Comments

@ziriax
Copy link

ziriax commented Jun 11, 2019

We get some unexpected regression compiler errors in our code base when upgrading to tsc 3.5.1.

We simplified the code for this bug report.

If this isn't a bug, it would be nice to know why this is now expected behavior, and what would be the recommended workaround?

TypeScript Version: 3.5.1

Search Terms:
3.5, 3.5.1, type guard

Code

enum PrimitiveKind {
  Float,
  Integer,
}

function test(kind: PrimitiveKind.Float) {
  console.log("bla");
}

function works(kind: PrimitiveKind) {
  if (kind === PrimitiveKind.Float) {
    test(kind);
  }
}

function kaput1<K extends PrimitiveKind>(kind: K) {
  if (kind === PrimitiveKind.Float) {
    test(kind);
  }
}

function kaput2<K extends PrimitiveKind>(kind: K & PrimitiveKind) {
  if (kind === PrimitiveKind.Float) {
    test(kind);
  }
}

function guard(kind: any): kind is PrimitiveKind {
  // Just for reporting the bug
  return true;
}

function kaput3<K extends PrimitiveKind>(kind: K & PrimitiveKind) {
  if (guard(kind) && kind === PrimitiveKind.Float) {
    test(kind);
  }
}

Expected behavior:

The compiles without errors (worked in 3.4.0)

Actual behavior:

Compiler error Type 'PrimitiveKind' is not assignable to type 'PrimitiveKind.Float'. for functions kaput1, kaput2 and kaput3.

(As Siegrift found out below, it seems this behavior started from version 3.4.5)

Playground Link:

Demo

Related Issues:

#31833 #31837

@Siegrift
Copy link

I am not sure why this doesn't work (and to my newbie understanding it should)... But I have found these workarounds

playground

@Siegrift
Copy link

@ziriax Btw. for me it won't compile even with TS version 3.4.5

@ziriax
Copy link
Author

ziriax commented Jun 11, 2019

@Siegrift

Indeed, we were still using TS 3.4.0, it fails with 3.4.5, nice catch!

function kaput4<K extends PrimitiveKind>(kind: PrimitiveKind) {
  switch (kind) {
    case PrimitiveKind.Float:
      test(kind);
      break;
  }
}

Thanks for the working code, it is amazing that a switch statement does work! Semantically this should be the same as an if statement no?

PS: Of course your function kaput4 is not kaput anymore 😉

@ziriax ziriax changed the title 3.5.1 breaks simple unambiguous type guards? 3.4.5+ breaks simple unambiguous type guards? Jun 11, 2019
@ziriax ziriax changed the title 3.4.5+ breaks simple unambiguous type guards? 3.5.1 breaks simple unambiguous type guards? Jun 11, 2019
@ahejlsberg
Copy link
Member

The sample code above has the same errors in every version of TypeScript going back 2.5, so whatever problem you have updating to 3.5.1, this isn't it.

@ahejlsberg ahejlsberg added the Needs More Info The issue still hasn't been fully clarified label Jun 11, 2019
@ziriax
Copy link
Author

ziriax commented Jun 11, 2019

Ouch, then we oversimplified the code that broke, mea culpa, I will revise.

@ahejlsberg
Copy link
Member

The new errors you're seeing are likely related to #30769 and may be intended.

@ziriax
Copy link
Author

ziriax commented Jun 11, 2019

@ahejlsberg You are 100% correct, the full signature of the code that breaks in our code is

function valueAt<K extends PrimitiveKind>(curve: AnyCurve<K>, time: Time): PrimitiveKindToType[K] {...}

From issue #30769 I can understand why this breaks now, but it is going to break a lot of our code base ;-)

@ziriax ziriax closed this as completed Jun 11, 2019
@Siegrift
Copy link

@ahejlsberg @ziriax Sorry, I am not able to see how it is related. And I am curious about why TS is able to infer the enum type when using switch but not when using if. Can you please explain? :)

@ahejlsberg
Copy link
Member

And I am curious about why TS is able to infer the enum type when using switch but not when using if. Can you please explain?

The example declares a type parameter but never uses it.

@Siegrift
Copy link

No, it uses it in the test function (which is ommited from the snippet). Here is a working playground

@ahejlsberg
Copy link
Member

@Siegrift Your playground example declares K type variables in the two functions and then proceeds to not use them. It makes no sense, but is working as intended. The errors in the original example were due to variables of a generic type not being narrowed--which is expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs More Info The issue still hasn't been fully clarified
Projects
None yet
Development

No branches or pull requests

3 participants