Home > front end >  Why won't typescript infer my second type argument?
Why won't typescript infer my second type argument?

Time:08-25

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

CodePudding user response:

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

CodePudding user response:

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

CodePudding user response:

Based on your type definition, there are two arguments:

useTypedRead = < Contract extends TypedContract , MethodName extends keyof Contract["functions"]>

but when you called it, you have used only one type:

useTypedRead<Erc20>

Hope this helps!

  • Related