Ed_
Ed_

Reputation: 19098

Why won't typescript infer my second type argument?

I'm trying to set up a type for a generic function. The second type argument should be inferred from the type of the second function argument.

It's not working, though, I think because of the way the type of the second argument also depends on the first type argument...

It's hopefully much easier to understand what I'm trying to achieve by looking at the code:

export type TypedContract = Contract & {
  functions: Record<string, (...args: any[]) => any>;
  filters: Record<string, (...args: any[]) => any>;
};

export const useTypedRead = <
  C extends TypedContract,
  MethodName extends keyof C["functions"]
>(
  address: string,
  method: MethodName,
  params: Parameters<C["functions"][MethodName]>
): void => {
  /** */
};

// When calling like this, I get "Expected 2 type arguments, but got 1"
useTypedRead<Erc20>("0xSomeAddress", "balanceOf");

Typescript playground here

Upvotes: 1

Views: 1143

Answers (2)

BorisTB
BorisTB

Reputation: 1806

You just have to use default value for next generics like this:

export const useTypedRead = <
  C extends TypedContract,
  MethodName extends keyof C["functions"] = keyof C["functions"]
>(
  address: string,
  method: MethodName extends keyof C["functions"] ? MethodName : never,
  params: Parameters<C["functions"][MethodName]>
): void => {
  /** */
};

Playground example

Upvotes: 1

jperl
jperl

Reputation: 5112

If you could also pass the contract object, typescript could infer it all for you. Like so:

export type TypedContract = {
  functions: Record<string, (...args: any[]) => any>;
  filters: Record<string, (...args: any[]) => any>;
};

export const useTypedRead = <
  Contract extends TypedContract,
  MethodName extends keyof Contract["functions"]
>(
  contract: Contract,
  address: string,
  method: MethodName,
  ...params: Parameters<Contract["functions"][MethodName]>
): void => {
  /** */
};

const myContract = {
  functions: {
    balanceOf: (foo: string, bar: string) => void (0)
  },
  filters: {
    foo: () => void (0)
  }
}

//this doesn't work
useTypedRead(myContract, "0xSomeAddress", "someFunction");

//this works as expected and we have to pass the two parameters
useTypedRead(myContract, "0xSomeAddress", "balanceOf", "foo", "bar");

Playground

Upvotes: 1

Related Questions