GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Intersecting discriminated union breaks type narrowing · Issue #9919 · microsoft/TypeScript

Intersecting discriminated union breaks type narrowing #9919

New issue
Copy link
New issue
Copy link
Closed
Closed
Intersecting discriminated union breaks type narrowing#9919
Copy link
Labels
FixedA PR has been merged for this issueA PR has been merged for this issueSuggestionAn idea for TypeScriptAn idea for TypeScript
Milestone
TypeScript 2.1

Description

@gcnew
Marin Marinov (gcnew)
opened on Jul 24, 2016
Issue body actions

TypeScript Version: nightly (2.0.0-dev.201xxxxx)

Code

type ToString = {
    toString(): string;
}

type BoxedValue = { kind: 'int',    num: number }
                | { kind: 'string', str: string }

type IntersectionFail = BoxedValue & ToString

type IntersectionInline = { kind: 'int',    num: number } & ToString
                        | { kind: 'string', str: string } & ToString

function getValueAsString(value: IntersectionFail): string {
    if (value.kind === 'int') {
        // Property 'num' does not exist on type '({ kind: "int"; num: number; } | { kind: "string"; str: string; }) & { toString(): string; }'.
        return '' + value.num;
    }

    // Property 'str' does not exist on type '({ kind: "int"; num: number; } | { kind: "string"; str: string; }) & { toString(): string; }'.
    return value.str;
}

If a discriminated union is enhanced via intersection, type narrowing based on the discrimination field starts to fail. This behaviour is not exhibited if a new discriminated union is created with the intersection being inlined into each of the respective options.

The function getValueAsString(value: IntersectionFail): string fails to compile, while getValueAsString(value: IntersectionInline): string compiles just fine. Unfortunately, it is IntersectionFail that is the result of natural program evolution.

Reacted by Anatoly Kudinov

Activity

  1. yortus commented on Jul 25, 2016

    @yortus
    Troy Gerwien (yortus)
    on Jul 25, 2016
    Contributor
    More actions

    Sounds like you want the compiler to be aware of the distributive law:

    T & (U | V)   ≡   (T & U) | (T & V)
  2. Daniel Rosenwasser (DanielRosenwasser)
    added
    SuggestionAn idea for TypeScriptAn idea for TypeScript
    In DiscussionNot yet reached consensusNot yet reached consensus
    on Jul 26, 2016
  3. jods4 commented on Aug 22, 2016

    @jods4
    jods (jods4)
    on Aug 22, 2016
    More actions

    Got caught by this today. I was unsure if it was too far-fetched to open an issue or not ;)

    Troy Gerwien (@yortus) I don't think about it as "distributive" law, although I guess you can see it that way.
    I am thinking of "intersection" types like "extensions" or types glued together. So it kind of makes sense that you can narrow any "part" of your intersection type.

    The tricky part with seeing it as "distributive" is that it may explode quickly, consider: (A|B) & (C|D) & (E|F|G) and you have a union type of 12 parts.
    But you can process it without combinatorial explosion. If you narrow the discriminant property of (C|D) to D only, you are left with (A|B) & D & (E|F|G).

  4. yortus commented on Aug 23, 2016

    @yortus
    Troy Gerwien (yortus)
    on Aug 23, 2016
    Contributor
    More actions

    The distributive law is just the 'set theory' principle that makes it valid. Whether the compiler expands all the terms or not is implementation detail, I guess with tradeoffs either way. As you point out it could create an explosion of types. On the other hand, doing a full expansion would allow complex types to be 'normalized', so you could tell when they are identical, eliminate redundant terms, etc.

  5. jods4 commented on Aug 23, 2016

    @jods4
    jods (jods4)
    on Aug 23, 2016
    More actions

    Troy Gerwien (@yortus) I'm nitpicking here: I think what the compiler is lacking is the distributivity of the narrowing operation. Because it doesn't seem to narrow intersection types and that's the problem in (A|B) & C and it would be the same problem in (A&C) | (B&C).

    It seems what the compiler lacks is knowledge that, if n() represents narrowing a type n(A & B) == n(A) & n(B). If this rule was applied by the compiler, both types constructions above would correctly be narrowed to A&C when n(A|B) == A.

  6. yortus commented on Aug 23, 2016

    @yortus
    Troy Gerwien (yortus)
    on Aug 23, 2016
    Contributor
    More actions

    This issue is about narrowing. I think we are saying the same thing.

  7. Igmat commented on Sep 28, 2016

    @Igmat
    Ihor Chulinda (Igmat)
    on Sep 28, 2016
    More actions

    Is here some progress? I've checked a lot of discussions, but haven't found any decision. Would be distributive law for unions and intersections implemented? And if yes, in what release we can expect that?

  8. ahejlsberg commented on Oct 18, 2016

    @ahejlsberg
    Anders Hejlsberg (ahejlsberg)
    on Oct 18, 2016
    Member
    More actions

    Fix now available in #11717.

  9. gcnew commented on Oct 19, 2016

    @gcnew
    Marin Marinov (gcnew)
    on Oct 19, 2016
    ContributorAuthor
    More actions

    Great news!

    Troy Gerwien (@yortus) turned out to be right! :) (in implementation sense, in logical there has never been any doubt)

  10. Anders Hejlsberg (ahejlsberg)
    closed this as completedin #11717on Oct 19, 2016
  11. Mohamed Hegazy (mhegazy)
    added
    FixedA PR has been merged for this issueA PR has been merged for this issue
    and removed
    In DiscussionNot yet reached consensusNot yet reached consensus
    on Oct 19, 2016
  12. Mohamed Hegazy (mhegazy)
    added this to the TypeScript 2.1 milestone on Oct 19, 2016
  13. microsoft
    locked and limited conversation to collaborators on Jun 19, 2018
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

    FixedA PR has been merged for this issueA PR has been merged for this issueSuggestionAn idea for TypeScriptAn idea for TypeScript

    Type

    No type

    Projects

    No projects

      Milestone

      • TypeScript 2.1

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.