Closed
Description
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 }
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