Home > database >  Infer function argument type in typescript
Infer function argument type in typescript

Time:04-29

I am trying to automatically infer function argument types as follows:

const FUNCTION_A = "a";
const FUNCTION_B = "b";

type FunctionKeyType = typeof FUNCTION_A | typeof FUNCTION_B;

function functionA(arg: string): string {
    return "A";
}

function functionB(arg: number): string {
    return "B";
}

type AnyFunctionType = (arg: any) => string;

const funcs: Record<FunctionKeyType, AnyFunctionType> = {
    [FUNCTION_A]: functionA,
    [FUNCTION_B]: functionB
}

console.log("A:", funcs[FUNCTION_A]("a")) // works
console.log("B:", funcs[FUNCTION_B](0))  // works
console.log("B:", funcs[FUNCTION_B]("fail")) // should fail typechecking but does not
  • There will be a large number of functions with unique arg types, but each will always have a single argument called arg and return a string
  • I need to have a mechanism for key => function, however it doesn't necessarily need to be a Record

Is there a way to write AnyFunctionType such that when I use a function via funcs["..."], it infers the type of the argument?

TS Playground

CodePudding user response:

When you declare the type as Record<string, AnyFunctionType> then all functions are assumed to be of type AnyFunctionType which takes any as an argument.

If you want to apply a constraint, but infer something more specific than that, then you need to use a generic function to make this object. (See issue#47920 for how this might be solved in the future)

function makeFuncs<T extends Record<string, AnyFunctionType>>(funcs: T) {
    return funcs
}

const funcs = makeFuncs({
    "a": functionA,
    "b": functionB
})

const badFuncs = makeFuncs({
    "a": functionA,
    "b": functionB
})

console.log("A:", funcs["a"]("a")) // works
console.log("B:", funcs["b"](0))  // works
console.log("B:", funcs["b"]("fail")) // error

And it enforces that the the function signature is correct:

const badFuncs = makeFuncs({
    "a": functionA,
    "b": () => 123 // type error
})

Playground

CodePudding user response:

You can make use of a Generic Function to implement this constraint.


type AnyFunctionType = (arg: any) => string;


function InferFunctionType<T extends {[k: string]:  AnyFunctionType }>(t: T){
    return t
}

const funcs =  InferFunctionType({
    "a": functionA,
    "b": functionB, 
})



console.log("A:", funcs["a"]("a")) // works
console.log("B:", funcs["b"](0))  // works
console.log("B:", funcs["b"]("fail")) // Here Typechecking gives error

Code Playground

CodePudding user response:

You've said the calls will be as shown, where the function name is a compile-time constant. That's good news! That means you can do it. :-) You can have a compile-time constant object to do the lookup on:

const funcs = {
    "a": functionA,
    "b": functionB
} as const; // <== Note

console.log("A:", funcs["a"]("a"));         // Works
console.log("B:", funcs["b"](0));           // Works
console.log("A wrong:", funcs["a"](0));     // Error as desired
console.log("B wrong:", funcs["b"]("x"));   // Error as desired

Playground link

You get full IDE support there as well, the intellisense tells you the argument type required.

  • Related