class Selector<Interface> {
public where<Key extends keyof Interface>(
key: keyof Pick<Interface, Key>,
value: Interface[Key]
) {
// [implementation]
}
}
// Multiple types to check the Pick
interface IBook {
author: string;
pagesCount: number;
wasRead: boolean;
}
const selector = new Selector<IBook>();Key (T) will dynamically create a generic for Pick from keys of Interface (U) and the type of value argument will be inferred from the key we pass to the method.
// notice:
key: keyof Pick<Interface, Key>We specifically have to narrow it down to just one key of U, which will effectively infer a single type (later in value argument), not all types from the interface if we leave just the key: keyof U.
Consider the other workarounds found somewhere on the Internet:
public test(key: keyof T, value: T[keyof T]) {}or even
public test<T extends keyof U>(key: keyof U, value: U[keyof T]) {}This gives no type safety since it accepts all types from U that can be unrelated to key, which is not what we want.
This can also work on generic functions:
function where<Key extends keyof IBook>(key: keyof Pick<IBook, Key>, value: IBook[Key]) {}
where("pagesCount", "2"); // Fails
where("pagesCount", 2); // Passes
where("pagesCount", false); // FailsI don't know how to make such function 100% generic, though. It could be something like this:
function where<U, Key extends keyof U>(key: keyof Pick<U, Key>, value: U[Key]) {}but then it would require such horrible syntax:
where<IBook, "author">("author", 1); // Fails, which is goodPS: I figured this out completely by accident, I don't know much about TypeScript


