Skip to content

Type Guards are no longer working correctly all the time #54178

Open
@MaksiRose

Description

@MaksiRose

Bug Report

πŸ”Ž Search Terms

Type Guard

πŸ•— Version & Regression Information

  • This changed between versions 4.9.5 and 5.0.4

⏯ Playground Link

A simple example

πŸ’» Code

declare class Foo<Stuff extends boolean> {
  stuff: Stuff extends true ? string : string | undefined;
  isReady(): this is Foo<true> 
}
declare const x: Foo<boolean>;
if(x.isReady()) {
  x
//^? - const x: Foo<boolean>
// Should be Foo<true>
}

πŸ™ Actual behavior

The Type Guard didn't change the type, which is unintuitive behavior.

πŸ™‚ Expected behavior

The method should act as a type guard and change the type

Activity

Andarist

Andarist commented on May 8, 2023

@Andarist
Contributor

Might be related to #53436

graphemecluster

graphemecluster commented on May 25, 2023

@graphemecluster
Contributor

The same bug happens in the code snippet from #9619 (comment) and it still presents in the latest nightly build.
Could anyone (perhaps @RyanCavanaugh?) please bisect it?

Andarist

Andarist commented on May 25, 2023

@Andarist
Contributor

@graphemecluster your issue (and this one here) is caused by: #52984 , the one that I linked to here (#53436) was broken before that though

added
Possible ImprovementThe current behavior isn't wrong, but it's possible to see that it might be better in some cases
on May 26, 2023
added this to the Backlog milestone on May 26, 2023
RyanCavanaugh

RyanCavanaugh commented on May 26, 2023

@RyanCavanaugh
Member

Foo<true> isn't considered a strict subtype of Foo<boolean> due to the variance measurement coming out too conservative when looking at stuff. TS doesn't have smarts to identify that a conditional type with T in the true branch and T | U in the false branch is actually covariant on the tested type parameter - I'm not entirely sure how feasible it would be to do that but it would at least be possible.

Counterexample that works:

declare class Foo<Stuff extends boolean> {
  observ: Stuff;
  isReady(): this is Foo<true>; 
}

declare const x: Foo<boolean>;
if (x.isReady()) {
  x;
  //^? - const x: Foo<true>
}

In this case you can write a variance annotation to make it work as desired:

declare class Foo<out Stuff extends boolean> {
graphemecluster

graphemecluster commented on May 27, 2023

@graphemecluster
Contributor

@RyanCavanaugh Understandable, but #52984 is still a breaking change.

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

    Possible ImprovementThe current behavior isn't wrong, but it's possible to see that it might be better in some cases

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @RyanCavanaugh@Andarist@graphemecluster@MaksiRose

        Issue actions

          Type Guards are no longer working correctly all the time Β· Issue #54178 Β· microsoft/TypeScript