Skip to content

Instantly share code, notes, and snippets.

@DarkStoorM
Created July 6, 2022 23:55
Show Gist options
  • Save DarkStoorM/2293b2f764d699355425d086f2f1f07e to your computer and use it in GitHub Desktop.
Save DarkStoorM/2293b2f764d699355425d086f2f1f07e to your computer and use it in GitHub Desktop.

TypeScript - infer type of second argument based on the first argument inside classes

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>();

img

img

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.

img

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); // Fails

I 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 good

PS: I figured this out completely by accident, I don't know much about TypeScript

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment