Skip to content

Refine unions in contextual typing #16457

Closed

Description

Source issue #16450 and others (TODO: find them)

This code should work, but doesn't:

interface X {
    type: 'x';
    value: string;
}

interface Y {
    type: 'y';
    value: 'none' | 'done';
}

function foo(bar: X | Y) { }

foo({
    type: 'y',
    value: 'done',
});

Similar example:

interface X {
    type1: 'x';
    value: string;
}

interface Y {
    type2: 'y';
    value: 'none' | 'done';
}

function foo(bar: X | Y) { }

foo({
    type2: 'y',
    value: 'done',
});

We can, in principle, make this work by discarding X from the union.

In the first example, this can be done (somehow) by using the discriminating union from the object literal.

In the second example, this can be done by noting that we have a fresh object literal type (meaning there are known to be no additional properties) and a missing type1 property.

Activity

  1. timwangdev commented on Jul 13, 2017

    @timwangdev

    +1
    Looking forward to see any progress on this!

  2. jebcat1982 commented on Jul 13, 2017

    @jebcat1982

    Microsoft has the current version at typescript.com

  3. mhegazy commented on Sep 12, 2017

    @mhegazy
    Contributor

    seems to be the same underlying issue as #18365

  4. sandersn commented on Sep 14, 2017

    @sandersn
    Member

    I have a fix for the first example, but note that the first example won't magically work because value looks like a discriminant too, albeit not a useful one. This fails with current narrowing as well:

    function f(xy: X | Y) {
      if (xy.value === 'done') {
        // xy: X | Y
      }
      else {
        // xy: X | Y
      }
    }

    That's because, as a discriminant, string | 'none' | 'done' boils down to just string.

  5. mhegazy commented on Oct 9, 2017

    @mhegazy
    Contributor

    Another alternative here is to always maintain the literal type if the contextual type is its base type. i.e. give value the type "done" even thought the contextual type is string.

  6. sandersn commented on Nov 1, 2017

    @sandersn
    Member

    I think #19587 will fix this.

  7. mifopen commented on May 24, 2018

    @mifopen

    Should this code work after fix?

    interface Control {
        CurrentMode: "View" | "Edit";
    }
    
    let controls: Control[];
    let controlsToEdit: Control[] = controls.map(c => c
        ? { CurrentMode: "Edit" }
        : c);

    For now (ts 2.8.3) I get Type 'string' is not assignable to type '"View" | "Edit"'

  8. mhegazy commented on May 24, 2018

    @mhegazy
    Contributor

    Slava (@mifopen) i think this is #11152

  9. locked and limited conversation to collaborators on Jul 31, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

SuggestionAn idea for TypeScript

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions