Turn function names, its arguments and return type of a class into a function parameters

48 views Asked by At

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.

0

There are 0 answers