How to infer type of function parameter that is called

Viewed 38

How can I get the compiler to infer T as 'string'? It is currently infered as unknown.

declare function a<T>(b: (c: (d: T) => void) => void): void;

a((d) => {d('')});

Also tried

declare function a<T extends (c: <U>(d: U) => void) => void, U extends T extends (c: (d: infer I) => void) => void ? I : never>(a: T): void;

a((d) => {d('')})

Still same result. If I manually specify the generic and do a<string>((d) => {d('')});, it will restrict the d function parameter to only accept string as expected.

1 Answers

I am not sure if understood correctly what you are trying to do, but I think that if you want to infer properly the type "string" for when you call c with the argument d, you need to adjust the position of the generic

declare function a(b: (c: <T>(d: T) => void) => void): void;

a((c) => {
  c("value");
});

Because it seems like you are interested in understand the meaning of the c function argument value more than the a function argument.

With the position of the generic modified c's callable will be inferred as c<string>.

enter image description here

As the documentation says:

Here we use type argument inference — that is, we want the compiler to set the value of Type for us automatically based on the type of the argument we pass in:

doc: https://www.typescriptlang.org/docs/handbook/2/generics.html

The documentation specifies that that the inference is given by the argument from where you set the generic.

In your case, if you set the generic in a, the compiler will try to infer the first argument which is a function, and then off course in the next level the argument of the callable c will be unknown. It is easier to understand if you decouple the types

// The argument of C will be the type of D
type InnerFunctionC<T> = (d: T) => void;

// The argument of C will be given by the argument of B
type InnerFunctionB<T> = (c: InnerFunctionC<T>) => void;

// The argument of A will be given to B
declare function a<T>(b: InnerFunctionB<T>): void;

The regular inference will do the following steps

// A receives a function as argument the T = FN
a(
  // The FN ( b ), "pass down" the inference to "c" = FN
  (c) => { // C inferences is = FN
    c("a"); // How can typescript know C inference beforehand with T "passed down" from A ?
  }
);

If you want to continue with this type, the only solution is as you said, override the inference by specifying the type. ( But I see no point in doing that )

a<string>((d) => {d('')});

Hope that this helped you!

Related