镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

aliasing discriminated union tag breaks case analysis #10830

Description

@Swatinem

TypeScript Version: 2.1.0-dev.20160909

Code

type A = {t: "a", a: string};
type B = {t: "b", b: string};
type U = A | B;

const a: U = {t: "a", a: "a"} as U;

if (a.t === "b") {
    a.b; // works as expected
}

const {t} = a;
if (t === "b") {
    a.b; // error TS2339: Property 'b' does not exist on type 'U'.
}

Expected behavior:

The type of a.t should correctly transfer to t when assigning or using destructuring. t should be usable as discriminant.

Actual behavior:

The type of t is widened from "a" | "b" to string and therefore can not be used as discriminant any more.

As the same happens also for flow (See facebook/flow#2409), it may be my thinking that is a little bit off. But I think that the case should be valid. The programmer should not be forced to repeat base. all over, that’s what variables are for.

Activity

  1. RyanCavanaugh commented on Sep 9, 2016

    @RyanCavanaugh
    Member

    The type of a.t should correctly transfer to t when assigning or using destructuring. t should be usable as discriminant.

    Tracking this correctly would require that variables "remember where they came from" and what implications that has on other variables. It'd probably be possible/reasonable to make that work at 1 level of indirection (rather than at 0 levels of indirection), but tracking it for N levels of indirection would be very complex, and then presumably you'd be surprised when it worked at 1 level of indirection but not 2.

    So given the choice between "be surprised at 2 and things get way more complex in the compiler implementation" vs "be surprised at 1", we're generally sticking with the latter.

  2. ligaz commented on Sep 15, 2016

    @ligaz

    What about the following case:

    const { type, payload } = action;
      switch (type) {
        case 'SOME_STRING':

    Is it too complex or there is possibility that you can add support for it?

  3. mhegazy commented on Sep 15, 2016

    @mhegazy
    Contributor

    Stefan Dobrev (@ligaz) this already works as intended:

    declare const action: { type: "A", payload: number } | { type: "B", payload: string };
    
    
    const { type, payload } = action;
    switch (type) {
        case 'SOME_STRING': // "SOME_STRING"" is not compatible with "A" | "B"
    
    }
  4. ligaz commented on Sep 16, 2016

    @ligaz

    The problem is that the payload is not typed if the tag is matched:

    declare const action: { type: "A", payload: number } | { type: "B", payload: string };
    
    const { type, payload } = action;
    switch (type) {
        case 'A': // payload type is 'number | string' not 'number'.
    
    }
  5. mhegazy commented on Sep 16, 2016

    @mhegazy
    Contributor

    There is no way to know that there is a relationship between the two variables.

  6. 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

    Fix AvailableA PR has been opened for this issueSuggestionAn idea for TypeScriptToo ComplexAn issue which adding support for may be too complex for the value it adds

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions