Skip to content

3.5.1 breaks simple unambiguous type guards? #31853

Closed

Description

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

Activity

  1. Siegrift commented on Jun 11, 2019

    @Siegrift

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

    playground

  2. Siegrift commented on Jun 11, 2019

    @Siegrift

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

  3. ziriax commented on Jun 11, 2019

    @ziriax
    Author

    Emanuel Tesař (@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 😉

  4. changed the title [-]3.5.1 breaks simple unambiguous type guards?[/-] [+]3.4.5+ breaks simple unambiguous type guards?[/+] on Jun 11, 2019
  5. changed the title [-]3.4.5+ breaks simple unambiguous type guards?[/-] [+]3.5.1 breaks simple unambiguous type guards?[/+] on Jun 11, 2019
  6. ahejlsberg commented on Jun 11, 2019

    @ahejlsberg
    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.

  7. ziriax commented on Jun 11, 2019

    @ziriax
    Author

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

  8. ahejlsberg commented on Jun 11, 2019

    @ahejlsberg
    Member

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

  9. ziriax commented on Jun 11, 2019

    @ziriax
    Author

    Anders Hejlsberg (@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 ;-)

  10. Siegrift commented on Jun 11, 2019

    @Siegrift

    Anders Hejlsberg (@ahejlsberg) Peter Verswyvelen (@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? :)

  11. ahejlsberg commented on Jun 11, 2019

    @ahejlsberg
    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.

  12. Siegrift commented on Jun 11, 2019

    @Siegrift

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

  13. ahejlsberg commented on Jun 11, 2019

    @ahejlsberg
    Member

    Emanuel Tesař (@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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Needs More InfoThe issue still hasn't been fully clarified

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions