1

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

2
  • Will the calls to the functions be hardcoded like that? funcs["a"]("a")? Or will the key be a plain string? Commented Apr 28, 2022 at 16:57
  • 1
    It will use typed string literals for keys as well as for accessing the functions (Edited post to reflect key types) Commented Apr 28, 2022 at 16:59

3 Answers 3

1

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

Sign up to request clarification or add additional context in comments.

Comments

1

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

Comments

0

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.

2 Comments

@t-j-crowder, but this won't stop us from assigning anything to funcs right? like const funcs = { a: () => Array<[]>, b: boolean } as const because nothing is constraining us from doing that, which is required in the problem statement I guess
@Bishwajitjha - Yes, it won't. No, I don't think that's required by the problem statement. :-) But it's up to the OP (and others using our answers) to decide for themselves what's appropriate for them.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.