Skip to content

Discriminated union not correctly narrowed by intersection #36323

@jtbandes

Description

@jtbandes

I am filing this issue to re-open the discussion from #13300 and #18210 which are now locked. While the former was closed as a duplicate of the latter, which itself was closed in #18438, this change did not actually fix the original issue in #13300. Here is an example:

type A = { key: "one", value: number } | { key: "two", value: string };

type A_one = A & { key: "one" };

function foo(a1: A_one): number {
    return a1.value;  // error
}

A_one should be typed as { key: "one", value: number }, because there are no other possibilities, but is actually typed as ({ key: "one", value: number } & { key: "one" }) | ({ key: "two", value: string } & { key: "one" }).


Motivation: https://stackoverflow.com/q/59835356/23649; code I was trying to write to solve this problem:

function foo<K extends UnionA["key"]>(val: UnionA & { key: K }): (UnionB & { key: K }) | undefined {
    const k: K = val.key;  // not strictly necessary, but just to help reduce complexity below
    for (let i = 0; i < b.length; i++) {
        if (b[i].key === k) {
            return b[i];
        }
    }
    return undefined;
}

Error message on the return line:

Type 'UnionB' is not assignable to type '({ key: "one"; value: boolean; } & { key: K; }) | ({ key: "two"; value: string; } & { key: K; }) | undefined'.
  Type '{ key: "one"; value: boolean; }' is not assignable to type '({ key: "one"; value: boolean; } & { key: K; }) | ({ key: "two"; value: string; } & { key: K; }) | undefined'.
    Type '{ key: "one"; value: boolean; }' is not assignable to type '{ key: "one"; value: boolean; } & { key: K; }'.
      Type '{ key: "one"; value: boolean; }' is not assignable to type '{ key: K; }'.
        Types of property 'key' are incompatible.
          Type '"one"' is not assignable to type 'K'.
            '"one"' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint '"one" | "two"'.(2322)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Fix AvailableA PR has been opened for this issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions