Skip to content

Assignment fails for types with conditional lookup for a union of a generic #32816

Closed

Description

TypeScript Version: Version 3.6.0-dev.20190810

Search Terms: union conditional lookup

Code

type Target<T> = T extends null ? null : T;
type Target2<T> = { "one": null, "two": T }[T extends null ? "one" : "two"];
type Target3<T> = [null, T][T extends null ? 0 : 1];

function tst<T extends string>() {
    // These two pass as expected:
    const case0: Target2<T | null> = 1 as any as Target2<T | null>;
    const case1: { prop: Target<T | null> } = 1 as any as { prop: Target<T | null> };
    const case2: { prop: Target2<T> } = 1 as any as { prop: Target2<T> };
    const case3: { prop: Target3<T> } = 1 as any as { prop: Target3<T> };

    // These two fail as expected:
    const case4: { prop: Target2<T> } = 1 as any as { prop: Target2<T | null> };
    const case5: { prop: Target3<T> } = 1 as any as { prop: Target3<T | null> };

    // These two are expected to pass, but fail:
    const case6: { prop: Target2<T | null> } = 1 as any as { prop: Target2<T | null> };
    const case7: { prop: Target3<T | null> } = 1 as any as { prop: Target3<T | null> };
}

Expected behavior:
Expected cases 6 and 7 to pass, as both sides of assignment supposed to be structurally identical.

Actual behavior:
Cases 6 and 7 fail. Example output:

error TS2719: Type '{ prop: { "one": null; "two": T | null; }["one" | (T extends null ? "one" : "two")]; }' is not assignable to type '{ prop: { "one": null; "two": T | null; }["one" | (T extends null ? "one" : "two")]; }'. Two different types with this name exist, but they are unrelated.
  Types of property 'prop' are incompatible.
    Type '{ "one": null; "two": T | null; }[T extends null ? "one" : "two"] | null' is not assignable to type 'null & { "one": null; "two": T | null; }[T extends null ? "one" : "two"]'.
      Type '{ "one": null; "two": T | null; }[T extends null ? "one" : "two"]' is not assignable to type 'null & { "one": null; "two": T | null; }[T extends null ? "one" : "two"]'.
        Type 'T | null' is not assignable to type 'null & { "one": null; "two": T | null; }[T extends null ? "one" : "two"]'.
          Type 'T' is not assignable to type 'null & { "one": null; "two": T | null; }[T extends null ? "one" : "two"]'.
            Type 'string' is not assignable to type 'null & { "one": null; "two": T | null; }[T extends null ? "one" : "two"]'.
              Type 'string' is not assignable to type 'null'.
                Type 'T' is not assignable to type 'null'.
                  Type '{ "one": null; "two": T | null; }[T extends null ? "one" : "two"]' is not assignable to type 'null'.
                    Type 'T | null' is not assignable to type 'null'.
                      Type 'T' is not assignable to type 'null'.
                        Type 'string' is not assignable to type 'null'.

Playground Link

Related Issues: #30152 looks similar

Activity

  1. AnyhowStep commented on Aug 12, 2019

    @AnyhowStep
    Contributor

    Feels a little bit like this #32442

    Except yours does not use function params.

  2. jack-williams commented on Aug 12, 2019

    @jack-williams
    Collaborator

    This is a consequence of #30769 and fundamentally the same issue as discussed in #31833, however the trigger is slightly different.

    The instantiation of the distributive conditional type T extends null ? "one" : "two" is "one" | (T extends null ? "one" : "two") as the conditional distributes over the union. The union key causes the index access type in the target type to simplify to an intersection (despite the property never being assigned), as per #30769.

  3. typescript-bot commented on Aug 19, 2019

    @typescript-bot
    Contributor

    This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

  4. locked as resolved and limited conversation to collaborators on Oct 21, 2025
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

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions