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?