I have the following code, which is correct but is tricky to maintain...
interface FuncContainer<F extends (..._: Array<any>) => any> {
func: F
args: Parameters<F>
}
const func: FuncContainer<typeof Math.min> = {
func: Math.min,
args: [3, 4]
}
console.log(func.func(...func.args))
const funcs: Array<FuncContainer<(..._: Array<any>) => any>> = [
{
func: Math.min,
args: [1, 2]
} as FuncContainer<typeof Math.min>,
{
func: fetch,
args: ["https://google.com"]
} as FuncContainer<typeof fetch>
]
console.log(funcs)
This compiles without errors. However, he problem is when I have Array<FuncContainer>
. I want to be able to put different functions with different param signatures in 1 array, so I made the function generic. I also want TS to ensure that the types within each array element are consistent. To do that, I have type assertions inside each individual array element, but that is error-prone as there is no warning if someone forgets to add the type assertion to a new element to an Array<FuncContainer>
. Without it, incorrect types for the args won't be caught.
const funcs2: Array<FuncContainer<(..._: Array<any>) => any>> = [
{
func: Math.min,
args: ["5", "6"] // should error, but doesn't
}
]
Is there a way to annotate the array so that each array element is type-checked for the concrete type within each array position, but doesn't require manual type assertions like this code snippet? Maybe some way with mapped types?
EDIT: changed unknown
to any
so it works in the playground
CodePudding user response:
To build on @T.J. Crowder's answer: Here is a solution which only requires a single generic function.
function createFuncs<
F extends ((...args: any[]) => any)[]
>(funcs: [...{ [K in keyof F]: [F[K], Parameters<F[K]>] }]) {
return funcs;
}
const funcs = createFuncs([
[Math.min, [1, 2]],
[fetch, ["https://google.com"]],
[Math.min, ["x"]], // <== Error as desired
]);