I am building a library and trying to turn following implementation of the interface
const testInterfaceImpl = {
testSN: (a: string, n: number) => { return "" },
testN: (n: number) => { return },
test: () => { return 0 }
}
into function where I developer is able to interact with testInterfaceImpl
using targetFunction
const [stringResult0] = targetFunction([ ["testSN", "", 6] ])
const [stringResult0, voidResult1, stringResult2] = targetFunction([ ["testSN", "john", 5], ["test"], ["testSN", "doe", 7] ])
const [numberResult0, _] = targetFunction([ ["testN", 5], ["testSN", 999999] ]) // TS should error on ["testSN", 5]
In other words, I am aiming to create types for function according to the supplied class/object.
- Function should be able to infer available function names of provided class, and arguments of provided function name and its return type as well
- Developer should be able to provide multiple variadic tuples with mentioned above
- Function returns an array of same length as arguments provided with typed results
- (Edit) Errors or IntelliSense should be able to provide type of arguments required by selected function(-name)
I have tried following
interface TestInterface {
testSN: (a: string, n: number) => string
testN: (n: number) => void
test: () => number
}
type FNArgTuple<FN extends keyof TestInterface> = [FN, ...Parameters<TestInterface[FN]>]
const testFn = <FN extends keyof TestInterface>(args: FNArgTuple<FN>) => {
return {} as ReturnType<TestInterface[FN]>
}
const returnsString = testFn(["testSN", "", 6])
const returnsVoid = testFn(["testN", 5])
const returnsNumber = testFn(["test"])
testFn(["testSN", ""]) // error: not assignable to ["testSN", string, number]
testFn(["test", 9]) // error: not assignable to ["test"]
testFn(["testN", ""]) // error: Type 'string' is not assignable to type 'number'
which works as expected but, of course, only accepts one function at a time. After many tries, I came up with function that can accept and return multiple functions.
const testInterfaceImpl: TestInterface = {
testSN: (a: string, n: number) => { return "" },
testN: (n: number) => { return },
test: () => { return 0 }
}
const targetFunction = <FN extends keyof typeof testInterfaceImpl>(args: FNArgTuple<FN>[]) => {
return args.map(([x, ...args]) => {
return testInterfaceImpl[x](...args) // error here, so I am not bothered about return types
})
}
targetFunction([["test"]])
targetFunction([["testSN", "", 6]])
targetFunction([["testSN", "", 6], ["test", "", 6]]) // should TS error on ["test", "", 6] but doesn't
targetFunction([["testN", 5], ["testSN", 5]]) // should TS error on ["testSN", 5] but doesn't
targetFunction([["testSN", ""], ["testN"]]) // error
targetFunction([["test", 9]]) // error
targetFunction([["testN", ""]]) // error
This is oversimplified version of what I am trying to achieve but covers everything that's important. Sharing Playground link with above code.
I see that I have created function where function names and their parameters are mixed from each other but I am not sure where to go from here. Any help is appreciated.