For fun, I wanted to try to see how well TypeScript 2.1 could model Svelte's component API.
interface Options<Data, Computed> {
data?: Data
computed?: Computed;
}
declare class Component<Data, Computed> {
constructor(options: Options<Data, Computed>);
get<K extends keyof (Data & Computed)>(key: K): (Data & Computed)[K];
}
let c = new Component({
data: {
hello: ""
}
});
c.get("hello");
// ~~~~~~~ error! '"hello"' is not assignable to type 'never'
Expected: No error.
Actual: The constaint of K on get appears to be 'never', and thus gives an error when calling c.get with any string.
We currently eagerly resolve a keyof T when T is not a type variable (a type parameter or an indexed access type involving type parameters). That's probably too conservative. You can cheat and make it work by introducing an intermediate type parameter:
get<T extends Data & Computed, K extends keyof T>(key: K): T[K];
Since T doesn't appear anywhere in the parameter list, inference will always just fall back to the constraint. But this indirection isn't quite right and shouldn't be necessary.
As I peeked through the source code, that's what I figured (I think the line was type.flags & TypeFlags.TypeParameter ? getIndexTypeForTypeParameter(<TypeParameter>type)), but I wasn't 100% sure. Thanks for clarifying!
this also should work:
declare class Component<Data, Computed> {
constructor(options: Options<Data, Computed>);
get<K extends keyof Data>(key: K): Data[K];
get<K extends keyof Computed>(key: K): Computed[K];
}
Most helpful comment
this also should work: