Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refine unions in contextual typing #16457

Closed
RyanCavanaugh opened this issue Jun 12, 2017 · 8 comments · Fixed by #19966
Closed

Refine unions in contextual typing #16457

RyanCavanaugh opened this issue Jun 12, 2017 · 8 comments · Fixed by #19966
Assignees
Labels
Suggestion An idea for TypeScript

Comments

@RyanCavanaugh
Copy link
Member

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.

@timwangdev
Copy link

+1
Looking forward to see any progress on this!

@jebcat1982
Copy link

Microsoft has the current version at typescript.com

@mhegazy
Copy link
Contributor

mhegazy commented Sep 12, 2017

seems to be the same underlying issue as #18365

@sandersn
Copy link
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.

@mhegazy
Copy link
Contributor

mhegazy commented Oct 9, 2017

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.

@sandersn
Copy link
Member

sandersn commented Nov 1, 2017

I think #19587 will fix this.

@mifopen
Copy link

mifopen commented May 24, 2018

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"'

@mhegazy
Copy link
Contributor

mhegazy commented May 24, 2018

@mifopen i think this is #11152

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