Skip to content

Intersection of enum union with literal is unexpectedly never #21998

Closed
@jcalz

Description

@jcalz

TypeScript Version: 2.8.0-dev.20180211

Search Terms: enum literal intersection never

Code

type VerifyExtends<A, B extends A> = true
type VerifyMutuallyAssignable<A extends B, B extends C, C=A> = true

// string enum
enum Bug {
  ant = "a",
  bee = "b"  
}

declare var witness: VerifyExtends<'a', Bug.ant> // okay, as expected
declare var witness: VerifyExtends<'b', Bug.ant> // error, as expected

declare var witness: VerifyMutuallyAssignable<Bug, Bug.ant | Bug.bee> // okay, as expected
declare var witness: VerifyMutuallyAssignable<Bug.ant, Bug.ant & 'a'> // okay, as expected

declare var witness: VerifyExtends<Bug, Bug.ant> // okay as expected
declare var witness: VerifyExtends<Bug & 'a', Bug.ant & 'a'> // error, not expected!!

declare var witness: VerifyMutuallyAssignable<Bug & 'a', never> // okay, not expected!!

// numeric enum
enum Pet {
  cat = 0,
  dog = 1  
}

declare var witness: VerifyExtends<0, Pet.cat> // okay, as expected
declare var witness: VerifyExtends<1, Pet.cat> // error, as expected

declare var witness: VerifyMutuallyAssignable<Pet, Pet.cat | Pet.dog> // okay, as expected
declare var witness: VerifyMutuallyAssignable<Pet.cat, Pet.cat & 0> // okay, as expected

declare var witness: VerifyExtends<Pet, Pet.cat> // okay, as expected
declare var witness: VerifyExtends<Pet & 0, Pet.cat & 0> // error, not expected!!

declare var witness: VerifyMutuallyAssignable<Pet & 'a', never> // okay, not expected!!

Expected behavior:
I expect that Bug & 'a' should reduce to Bug.ant, or at least to Bug.ant | (Bug.bee & 'a').
Similarly, Pet & 0 should reduce to Pet.cat, or at least to Pet.cat | (Pet.dog & 0).

Actual behavior:
Both Bug & 'a' and Pet & 0 reduce to never, which is bizarre to me. I was trying to solve a StackOverflow question and realized that my solution was narrowing literals to never after a type guard. Something like:

declare function isBug(val: string): val is Bug
declare const a: "a" 
if (isBug(a)) {
  a // never?!
}

Thoughts?

Playground Link:

Related Issues:
I'm really not finding any, after searching for an hour. A few near misses but nothing that seems particularly relevant.

Metadata

Metadata

Assignees

No one assigned

    Labels

    In DiscussionNot yet reached consensusSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions