Search code examples
typescript

Function overload types are not working with my defined type


I can't get this to work at all:

type Method = {
    (pathname: string, handler: CallableFunction): void,
    (pathname: string, middleware: CallableFunction[], handler: CallableFunction): void,
}

type Route = {
    method: string,
    pathname: string,
    middleware?: CallableFunction[],
    handler: CallableFunction,
}

const routes: Route[] = []

export const get: Method = (param1: string, param2: CallableFunction | CallableFunction[], param3?: CallableFunction) => {
    push('GET', param1, param2, param3)
}

export const post: Method = (param1: string, param2: CallableFunction | CallableFunction[], param3?: CallableFunction) => {
    push('POST', param1, param2, param3)
}

const push = (method: string, param1: string, param2: CallableFunction | CallableFunction[], param3?: CallableFunction) => {
    routes.push({
        method: method,
        pathname: param1,
        middleware: typeof param2 == CallableFunction[] ? param2 : undefined,
        handler: typeof param2 == CallableFunction ? param2 : param3,
    })
}

Errors:

Type 'CallableFunction | CallableFunction[] | undefined' is not assignable to type 'CallableFunction[] | undefined'. 'CallableFunction' only refers to a type, but is being used as a value here.

How do I check if they are the exact type I am using? Or do I need to be less strict?

Also, is there a way to implement DRY principles so I don't have to repeated the same thing for every route method? See get, post, etc.

I tried using typeof param2 == 'function' but have no idea how to use it for an array of callable functions, etc.

Ideally I want to be able to check the exact types of the overloads...

Edit:

Guess I just have to use as?:

    if (param3) {
        routes.push({
            method: method,
            pathname: param1,
            middleware: param2 as CallableFunction[],
            handler: param3,
        })
    }

Solution

  • You can't do a typeof check against a typescript type, since it doesn't actually exist during runtime. I guess you could do something like this...

    Example

    type Method = {
        (pathname: string, handler: CallableFunction): void,
        (pathname: string, middleware: CallableFunction[], handler: CallableFunction): void,
    }
    
    type Route = {
        method: string,
        pathname: string,
        middleware?: CallableFunction[],
        handler: CallableFunction,
    }
    
    const routes: Route[] = []
    
    export const get: Method = (param1: string, param2: CallableFunction | CallableFunction[], param3?: CallableFunction) => {
        push('GET', param1, param2, param3)
    }
    
    export const post: Method = (param1: string, param2: CallableFunction | CallableFunction[], param3?: CallableFunction) => {
        push('POST', param1, param2, param3)
    }
    
    const push = (method: string, param1: string, param2: CallableFunction | CallableFunction[], param3?: CallableFunction) => {
        const maybeMiddleware = Array.isArray(param2) ? param2 : undefined;
        // `param3 ||` is saying if param3 has a value (using falsey check), then honor that since it'll always be a CallableFunction
        // next bit `Array.isArray(param2) ? param2[0] : param2` is saying in the event param2 _is_ an array, then we take the first item in the array, otherwise use that CallableFunction
        const handler: CallableFunction = param3 || (Array.isArray(param2) ? param2[0] : param2);
    
        routes.push({
            method: method,
            pathname: param1,
            middleware: maybeMiddleware,
            handler,
        })
    }