uPaymeiFixit
uPaymeiFixit

Reputation: 375

Why does TypeScript pick the wrong overloaded function?

I'm using promisify with mysql2's execute method, and when I do so it always seems to pick the last overload even if it doesn't match the parameters I've provided. I've reduced the problem down to this example to hopefully make it easier to understand and resolve.

In my example, execute has two overloads with the only differences being these required arguments:

  1. sql: string, and values: any
  2. options: Record<string, unknown>

After promisifying execute and passing it 'sql', ['values'] (which matches the first overload), I get an error Expected 1 arguments, but got 2. and it's clear that TypeScript is expecting me to use the second overload with a single required argument.

Why is this happening? Am I not understanding something?

Run on TypeScript Playground

// My custom promisify method. I get the same problem with the official one: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/node/util.d.ts#L1136
function promisify35<T extends any[], TResult>(
  fn: (...args: [...T, (err: any, result: TResult) => void]) => void,
): (...args: T) => Promise<TResult> {
  return (...args: T) => {
    return new Promise<TResult>((resolve, reject) => {
      fn(...args, (error, result) => {
        if (error) {
          reject(error);
        } else {
          resolve(result);
        }
      });
    });
  };
}

// My simplified version of mysql2's ExecutableBase https://github.com/sidorares/node-mysql2/blob/master/typings/mysql/lib/protocol/sequences/ExecutableBase.d.ts
export declare function ExecutableBase<T>(): {
  new (...args: any[]): {
    execute<T>(
      sql: string,
      values: any,
      callback?: (err: Error | null, result: T) => any,
    ): void;
    execute<T>(
      options: Record<string, unknown>,
      callback?: (err: Error | null, result: T) => any,
    ): void;
  };
} & T;

async function testbed() {
  const a = new (ExecutableBase())();

  // ✅ This works, it picks the correct (first) overload
  a.execute('hello', ['values']);

  // ❌ This does not work, it picks the incorrect (last) overload
  await promisify35(a.execute)('sql', ['values']);
}

Note: I'm aware of and would love to use mysql2/promise, but can't use it for a couple reasons.

Upvotes: 1

Views: 89

Answers (0)

Related Questions