Skip to content

Incorrect union type inferred #17930

Closed
Closed
@pelotom

Description

@pelotom

I've found it frequently desirable to be able to "look up" the union variant associated with a type tag. For example, given a union

type Foo = { tag: 'n'; val: number } | { tag: 's'; val: string }

one wants to be able to write a type operator

type Lookup<T extends Foo['tag']> = // ???

such that Lookup<'n'> = number and Lookup<'s'> = string.

I don't think this is possible in the current type system (would welcome correction on that point). Instead, we can use the trick of starting with a Lookup type, and deriving the union type from it:

type Lookup = {
  n: number
  s: string
}

type Foo = {
  [T in keyof Lookup]: { tag: T; val: Lookup[T] }
}[keyof Lookup]

then the inferred type of Foo is

type Foo = {
    tag: "n";
    val: number;
} | {
    tag: "s";
    val: string;
}

as desired, and we have our lookup type: Lookup['n'] = number and Lookup['s'] = string.

So I use this pattern a lot, and I wanted to generalize it:

type Unionize<Lookup> = {
  [T in keyof Lookup]: { tag: T; val: Lookup[T] }
}[keyof Lookup]

Unfortunately, this doesn't do what you want:

type Foo = Unionize<{
  n: number
  s: string
}>

Here, Foo is inferred to be

type Foo = {
    tag: "n" | "s";
    val: string | number;
}

This seems like a bug!

Metadata

Metadata

Assignees

No one assigned

    Labels

    FixedA PR has been merged for this issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions