Skip to content

Indexed access behavior differs between type aliases and type parameters. #16036

Closed
@hearnden

Description

@hearnden

TypeScript Version: 2.3.2

Summary: Tagged<Opts>[keyof Opts] evaluates differently depending on whether Opts is a type alias or a type parameter.

Code

// Given an index type:
type MyOpts = {
  x: number,
  y: string,
};

// pair each value with its index tag:
type Tagged<T> = { [K in keyof T]: { tag: K, val: T[K] } };
// which produces:
//   Tagged<MyOpts> == {
//     x: { tag: 'x', val: number },
//     y: { tag: 'y', val: string }
//   }

// Then project the signature of that mapped type to get a tagged/disjoint union:
type MyTaggedUnion1 = Tagged<MyOpts>[keyof MyOpts];
// which produces:
//   MyTaggedUnion1 = { tag: 'x', val: number } | { tag: 'y', val: string }
// which is exactly what we want.

// But when trying to express Tagged<MyOpts>[keyof MyOpts] as a generic type expression,
// the result turns out to be not as specific:
type TaggedUnion<Opts> = Tagged<Opts>[keyof Opts];
type MyTaggedUnion2 = TaggedUnion<MyOpts>;
// produces:
//   MyTaggedUnion2 = { tag: 'x' | 'y', val: number | string }

Playground link

Expected behavior:
Tagged<O>[keyof O] should produce the same thing, whether O is an alias to a type literal or the name of a type parameter.

Actual behavior:
The literal version produces the desirable result:

{ tag: 'x', val: number } | { tag: 'y', val: string }

The parameterized version produces the weaker result:

{ tag: 'x' | 'y', val: number | string }

Possibly related: #15983

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design LimitationConstraints of the existing architecture prevent this from being fixedFixedA 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