George
George

Reputation: 1050

define type for function with overloads

I am trying to create a global.d.ts file for a vector math lib I am using. There are some functions that can take different sets of arguments. For example:

function add(x, y, returnNew) {
  if (typeof x != 'number') {
    returnNew = y;
    if (isArray(x)) {
      y = x[1];
      x = x[0];
    } else {
      y = x.y;
      x = x.x;
    }
  }

  x += this.x;
  y += this.y;


  if (!returnNew) {
    return this.set(x, y);
  } else {
    // Return a new vector if `returnNew` is truthy
    return new (this.constructor)(x, y);
  }
}

and the docs describe it as

add(x, y [, returnNew]) or
add(array, [, returnNew]) or
add(vec2 [, returnNew])

And it always return a vector object

How can i define such type or interface to match this behaviour?

i tried this

type addFunction = (
    ((x: number, y: number, returnNew?: boolean) => Vec2) |
    ((vec: Vec2, returnNew?: boolean) => Vec2) |
    ((pos: [number, number], returnNew?: boolean) => Vec2)
)

and there are no errors, but when i use it

vector.add(2, 4)

it complains Argument of type '2' is not assignable to parameter of type 'number & Vec2 & [number, number]'

What am I missing? Please note that this is a third party lib and I can write typings only and I can't touch the lib code.

Upvotes: 0

Views: 82

Answers (2)

Matěj Pokorný
Matěj Pokorný

Reputation: 17895

Function overloading in Typescript works, but you must check what overload is used manually. So for your example...

function add(array: [number, number], returnNew?: boolean);
function add(vec2: { x: number, y: number }, returnNew?: boolean)
function add(x: number, y: number, returnNew?: boolean);
function add(
    param1: [number, number] | { x: number, y: number } | number,
    param2?: number | boolean,
    param3?: boolean) {
    if (Array.isArray(param1)) {
        // overload 1
    }
    else if (typeof param1 === 'object') {
        // overload 2
    }
    else {
        // overload 3
    }
}

Upvotes: 0

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 250376

What you defined is a union of function signatures. This is different than a function with overloads. A union of functions means that the implementing function could have any one of those signatures but not all of the. This is why when calling the arguments must be compatible with all arguments, as the argument should be valid for any one of the possible functions that could be assigned.

The syntax for a function signature with overloads is slightly different:

type addFunction = {
    (x: number, y: number, returnNew?: boolean): Vec2
    (vec: Vec2, returnNew?: boolean): Vec2
    (pos: [number, number], returnNew?: boolean): Vec2
}

An intersection instead of a union will also act as an overloaded signature:

type addFunction = (
    ((x: number, y: number, returnNew?: boolean) => Vec2) &
    ((vec: Vec2, returnNew?: boolean) => Vec2) &
    ((pos: [number, number], returnNew?: boolean) => Vec2)
)

Upvotes: 2

Related Questions