Assume that we have some class that has an important generic variable T
and another class we have two fields, one wrapped, and one not:
class Wrapper<V> {
constructor(public value: V) {
}
clone(): Wrapper<V> {
return new Wrapper(this.value);
}
}
class SomeClass {
value1 = new Wrapper(1);
value2 = 2;
}
Then, we want a method wrapperValue
which, when given an object (obj
) and a field name (name
) returns the value of wrapper accessed by obj[name].value
. It is important that the return type is correct. So far, this is what I have managed to come up with:
type WrapperKeyOf<S> = keyof {
[K in keyof S as S[K] extends Wrapper<any> ? K: never]: any
}
type WrapperValueTypeOf<W> = W extends Wrapper<infer V> ? V : never;
function wrapperValue<S, K extends WrapperKeyOf<S>>(
obj: S,
name: K,
): WrapperValueTypeOf<S[K]> {
const wrapper: Wrapper<WrapperValueTypeOf<S[K]>> = obj[name];
return wrapper.value;
}
wrapperValue(new SomeClass(), "value1");
The type WrapperKeyOf
restricts name
to only to be keys of S
where S[T]
is a Wrapper
, and WrapperValueTypeOf<S[T]>
gets the wrapper type.
The TypeScript compiler produces the following error:
Type 'S[K]' is not assignable to type 'Wrapper<WrapperValueTypeOf<S[K]>>'.
Type 'S[keyof { [K in keyof S as S[K] extends Wrapper<any> ? K : never]: any; }]' is not assignable to type 'Wrapper<WrapperValueTypeOf<S[K]>>'.
Type 'S[string] | S[number] | S[symbol]' is not assignable to type 'Wrapper<WrapperValueTypeOf<S[K]>>'.
Type 'S[string]' is not assignable to type 'Wrapper<WrapperValueTypeOf<S[K]>>'.
It seems that the fact that K
had to be a key of S
which accessed a Wrapper
gets lost. Is there any way to preserve this information somehow?