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 uniquearg
types, but each will always have a single argument calledarg
and return astring
- I need to have a mechanism for
key
=>function
, however it doesn't necessarily need to be aRecord
Is there a way to write AnyFunctionType
such that when I use a function via funcs["..."]
, it infers the type of the argument?
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
})
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
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
You get full IDE support there as well, the intellisense tells you the argument type required.