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

Partially overlapping union discriminators are not assignable from a union of discriminating values #45230

Description

Bug Report

🔎 Search Terms

tagged, discriminated, union, partially, overlapping, infinite, types, not, assignable

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about Type system behavior and Classes

I tried versions from v3.3.3 to v4.3.5, as those were available in the Playground at this time.

Note that between v3.3.3 and v3.5.1 something changed that affects type C from my example below. In v3.3.3 the assignment to const c: C is an error, from v3.5.1 its okay.

⏯ Playground Link

Playground link with relevant code

💻 Code

// Original Problem:
type A = { aprop: 'a' | RegExp } | { aprop: 'b' | RegExp };
declare const aprop: 'a' | 'b';
const a: A = { aprop }; // error

// Distillation:
type B = { bprop: 1 | string } | { bprop: 2 | string }
declare const bprop: 1 | 2;
const b: B = { bprop } // same error

// error appears only for "infinite" types, this works fine
type C = { cprop: 1 | 3 } | { cprop: 2 | 3 }
declare const cprop: 1 | 2;
const c: C = { cprop } // C['cprop'] == 1 | 2 | 3

// more detailed example
type Infinite = string;
type D = { dprop: 1 | Infinite } | { dprop: 2 | Infinite }
type Dprop = D['dprop']; // 1 | 2 | string -> "merging" seems to work correctly
declare const dprop: 1 | 2;
const d1: D = { dprop: 1 } // OK
const d2: D = { dprop: 2 } // OK
const d3: D = { dprop } // error

🙁 Actual behavior

The assignments to const a: A, const b: B, and const d3: D raise an error.

🙂 Expected behavior

As demonstrated in the assignments to const d1: D and const d2: D, my partially discriminating property dprop can be either of the two discriminating values. So It should be possible to assign to dprop a value that is either of them, as in the assignment to const d3: D.

Activity

  1. jcalz commented on Jul 29, 2021

    @jcalz
    Contributor

    between v3.3.3 and v3.5.1 something changed

    The change was "smarter union type checking", implemented in #30779 and released in TS3.5.

  2. RyanCavanaugh commented on Jul 29, 2021

    @RyanCavanaugh
    Member

    In general if you have some type { a: U1, b: U2, c: U3, d, U4 }, you can't tell if that's going to be assignable to some target type without doing a computation involving size(U1) * size(U2) * size(U3) * ... different expansions of the type to enumerate all possible inhabitants. So we're always going to sometimes reject some of these assignments because the special-casing to allow the trivial cases has some limits.

    That said, I don't see why this should matter when a non-literal type appears in the target.

  3. added
    Design LimitationConstraints of the existing architecture prevent this from being fixed
    and removed
    Needs InvestigationThis issue needs a team member to investigate its status.
    on Oct 23, 2024
  4. rbuckton commented on Oct 23, 2024

    @rbuckton
    Contributor

    This is the expected behavior. It is far too expensive to compute this kind of overlapping assignability for all cases, so we only currently do this work when a union has overlapping discriminant properties. A property is a discriminant property when its type consists of only unit types (e.g., 1 or "foo"). In the three failing cases above, the property in question consists of a unit type unioned with a non-unit type (string, RegExp, etc.), which means the property is not considered a discriminant and thus does not qualify for this assignability rule.

    In the case of C, both types in the union contain discriminant properties as they consist only of unit types or unions of unit types (1 | 3 and 2 | 3).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Design LimitationConstraints of the existing architecture prevent this from being fixedRescheduledThis issue was previously scheduled to an earlier milestone

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions