Skip to content

Type guard of union with conditional type not working since 4.3 #44382

Closed
@pedro-pedrosa

Description

@pedro-pedrosa

Bug Report

πŸ”Ž Search Terms

type guard conditional type

πŸ•— Version & Regression Information

  • This changed between versions 4.2.3 and 4.3.2

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

interface A<S> {
    a: S
}
interface B<S> {
    b: S
}
interface C<S> {
    c: S
}

type U1<S> = A<S> | B<S> | C<S>
function f1<S>(u: U1<S>): u is B<S> | C<S> {
    return false
}
function test1<S>(x: U1<S>) {
    if (!f1(x)) {
        x.a //OK
    }
}

type Cond<S> = S extends number ? B<S> : C<S>
type U2<S> = A<S> | Cond<S>
function f2<S>(u: U2<S>): u is Cond<S> {
    return false
}
function test2<S>(x: U2<S>) {
    if (!f2(x)) {
        x.a //ERROR
    }
}

πŸ™ Actual behavior

The type guard is unable to narrow the type of x to A

πŸ™‚ Expected behavior

The type guard should narrow the type of x to A because it is not assignable to Cond<S>

Activity

added
Working as IntendedThe behavior described is the intended behavior; this is not a bug
BugA bug in TypeScript
and removed
Working as IntendedThe behavior described is the intended behavior; this is not a bug
on Jun 10, 2021
added this to the Backlog milestone on Jun 10, 2021
bbenezech

bbenezech commented on Jun 17, 2021

@bbenezech

@RyanCavanaugh Can this bug be prioritize for 4.3.4 please? πŸ™
It's a real regression over 4.2. Inferred types are not narrow-able anymore.

RyanCavanaugh

RyanCavanaugh commented on Jun 17, 2021

@RyanCavanaugh
Member

This doesn't meet our bar for a patch release, but I'll move it to 4.4

andrewbranch

andrewbranch commented on Jul 23, 2021

@andrewbranch
Member

This was caused by #43183, because the conditional type Cond<S> gets substituted for its constraint, B<S> | C<S>, in the type of x before it gets narrowed, so we are effectively trying to narrow from A<S> | B<S> | C<S>. Most of the time, that’s way more useful than trying to narrow with Cond<S>, because it means you can actually discriminate between the two branches of the conditional. Cond<S> just won’t relate with many useful types during narrowing. However, it will relate to itself, which is what your example is doing, but in 4.3 we can no longer tell that’s what’s happening since we substituted the constraint earlier on.

I don’t know if it would make sense for your real code @pedro-pedrosa, but a workaround would be to expand the conditional type to a union in your type predicate:

function f2<S>(u: A<S> | B<S> | C<S>): u is B<S> | C<S> {
    return false
}

The only way I can think to fix this without undoing the very useful improvements of #43183 would be to carry the original, non-substituted type through narrowing (in addition to the usually-more-narrowable constraint-substituted type), trying it if narrowing the constraint-substituted type had no effect. But that sounds like it could be expensive, for what feels like quite an edge case to me.

pedro-pedrosa

pedro-pedrosa commented on Jul 26, 2021

@pedro-pedrosa
Author

I don’t know if it would make sense for your real code @pedro-pedrosa, but a workaround would be to expand the conditional type to a union in your type predicate:

Fortunately I was able to change it to u is A<S> because in my real example I wasn't exactly trying to figure out if my values where Cond<S>, I was trying to figure out if they were not Cond<S>. I didn't think of writing the guards this way because my real example is not just A | Cond, it has a few more types in that union other than A so it was just easier to do u is Cond<S> and negate it.

Thanks for the suggestion.

added
Needs InvestigationThis issue needs a team member to investigate its status.
and removed
BugA bug in TypeScript
on Aug 20, 2021
removed this from the TypeScript 4.4.1 (RC) milestone on Aug 20, 2021

15 remaining items

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

    Design LimitationConstraints of the existing architecture prevent this from being fixed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @bbenezech@pedro-pedrosa@andrewbranch@RyanCavanaugh

        Issue actions

          Type guard of union with conditional type not working since 4.3 Β· Issue #44382 Β· microsoft/TypeScript