Closed
Description
TypeScript Version: 2.7.0-dev.20171126
Context: a method for ember.js which extracts the value from some path, e.g.
get({ foo: { bar: { baz: 'hello' }}}, 'foo', 'bar', 'baz') // returns "hello"
Each value in the path might be some plain object T
or a Wrapped<T>
(in which case we return the underlying T
)
Code
Typescript can infer the returned type using a union:
// working example
interface Wrapped<T> { value: T }
type Props1<T> = {
[K in keyof T]: Wrapped<T[K]> | T[K];
}
declare function get1<T, K1 extends keyof T>(obj: Props1<T>, k1: K1): T[K1];
declare const obj1: { a: string };
const v1: string = get1(obj1, 'a'); // works
declare const obj2: { a: Wrapped<string> };
const v2: string = get1(obj2, 'a'); // works
However, it no longer works when the union types are nested:
// non-working example
type Props2<T> = {
[K in keyof T]: Wrapped<Props1<T[K]>> | Props1<T[K]>
};
declare function get2<T, K1 extends keyof T, K2 extends keyof T[K1]>(obj: Props2<T>, k1: K1, k2: K2): T[K1][K2];
declare const obj3: { a: { b: string } };
const v3: string = get2(obj3, 'a', 'b'); // works
declare const obj4: { a: Wrapped<{ b: Wrapped<string> }> };
const v4: string = get2(obj4, 'a', 'b'); // TS2345:Argument of type '"b"' is not assignable to parameter of type 'never'.
If I eliminate the right side of the union from Props2
, then the obj4
example works
type Props2<T> = {
[K in keyof T]: Wrapped<Props1<T[K]>> //| Props1<T[K]>
};
The obj4
example should pick the left side of the union to begin with