-
Notifications
You must be signed in to change notification settings - Fork 13k
Description
When trying to compose generic type aliases I've run into some instances where the compiler breaks the intended semantics. Here is as small/self-contained an example as I can come up with:
TypeScript Version: 2.4.0 / nightly (2.5.0-dev.20170627)
Code
// Let's define some generic type aliases:
// union of values of T:
type Valueof<T> = T[keyof T];
// mapped keys of properties:
type MappedKeyof<T> = {
[K in keyof T]: keyof T[K]
}
// and a concrete type to act on:
type Foo = {
one: { prop1: string },
two: { prop2: number }
}
// We want the union of all keys of properties of Foo, namely: 'prop1' | 'prop2'
type KeyofPropertyofFoo = Valueof<MappedKeyof<Foo>> // 'prop1' | 'prop2'; correct
// hey, this "union of keys of properties of" operation would be useful for me,
// let's define the composition as follows:
type KeyofPropertyof<T> = Valueof<MappedKeyof<T>>;
// apply it to foo, and... it is broken!
type KeyofPropertyofFooBroken = KeyofPropertyof<Foo> // never; broken
Expected behavior:
I expect KeyofPropertyof<Foo>
to be equivalent to Valueof<MappedKeyof<Foo>>
.
Actual behavior:
What's KeyofPropertyof<T>
doing? Quickinfo says type KeyofPropertyof<T> = keyof (T[keyof T])
. So it took Valueof<MappedKeyof<T>>
and "simplified" it to keyof Valueof<T>
, but that's the intersection of the keys of the properties, not the union. Hence the never
instead of 'prop1'|'prop2'
🙁
In general, given
type A = ...
type F<T> = ...
type G<T> = ...
type FoG<T> = F<G<T>>
I expect F<G<A>>
to be equivalent to FoG<A>
. Anything else makes it very hard to reason about what is going on.
This seems like something @tycho01 has run into before: is it the same issue as #16244 and/or #16018? Is it a bug? design limitation? intention? there any way to work around it in general? My use cases here are mostly type manipulation in the absence of built-in subtraction/exact/conditional mapped types. Someone on Stack Overflow asks a question, I try to build something that solves the problem, and I hit this.
Thoughts? Thanks!