Dmitry
Dmitry

Reputation: 1

Creating a generic proxy function in TypeScript

Let's say I want to create a generic proxy function like this:

type Callback<F extends (...args: any) => any> = F extends (...args: infer P) => infer R 
? (
    P extends [] ? (onComplete?: (result: R) => void) => void : (parameters: P, onComplete?: (result: R) => void) => void
)
: never;

function  createCallback<P extends unknown[], R, F extends (...args: P) => R>(func: F): Callback<F> {
    return (parameters?: P | ((result: R) => void), onComplete?: (result: R) => void ): void => {
        if (Array.isArray(parameters)) {
            R result = func(...parameters);
            if (onComplete) {
                onComplete(result);
            }
        } else if (typeof parameters === "function") {
            R result = func();
            parameters(result);
        }
    }
}

If the passed function accepts some parameters, then the returned function should have a signature (parameters: [<tuple of function parameters], optional onComplete callback). If the passed function does not accept any parameters, then the returned function should have a signature with a single optional onComplete callback.

Is it possible to do this in TypeScript? The above code won't compile with the error message saying the returned function is not assignable to type Callback.

Upvotes: 0

Views: 523

Answers (1)

Jean-Alphonse
Jean-Alphonse

Reputation: 816

If you're confident that createCallback does what it's supposed to, it's reasonable to do
return (...) as any as Callback<F>

Alternatively, this makes the compiler happy (playground)

type Callback<F extends (...args: any) => any> = F extends (...args: infer P) => infer R
    ? (...args: PossibleArguments<P, R>) => void
    : never;

type PossibleArguments<P, R, OnComplete = (result: R) => void> =
    [] extends P
    ? ([] | [onComplete: OnComplete])
    : ([parameters: P] | [parameters: P, onComplete: OnComplete])
// or
type PossibleArguments<P, R> = [...parameters: [] extends P ? [] : [parameters: P], onComplete?: (result: R) => void]

function createCallback<P extends unknown[], R>(func: (...args: P) => R): Callback<(...args: P) => R> {
    return ((...args: PossibleArguments<P, R>): void => {
        // ...
    })
}

It makes the signature of callbacks ugly, but autocomplete when calling them still works

Upvotes: 1

Related Questions