SpacePotatoes
SpacePotatoes

Reputation: 859

Overloading a recursive function in Typescript with Array and TypedArray

I have a recursive function with 2 overloads :

export function select(
  array: Float32Array,
  first: number,
  nth: number,
  last: number,
  comp: (a: number, b: number) => boolean,
): void;
export function select<T>(
  array: T[],
  first: number,
  nth: number,
  last: number,
  comp: (a: T, b: T) => boolean,
): void;
export function select<T>(
  array: Float32Array | T[],
  first: number,
  nth: number,
  last: number,
  comp: (a: number | T, b: number | T) => boolean,
): void {
  // Implementation of Floyd-Rivest algorithm
  // Some code
  select(array, newFirst, nth, newLast, comp);
  // Some code
}

Typescript complains about variable array when calling select function recursively in the implementation :

The argument of type 'Float32Array | T[]' is not assignable to the parameter of type '(number | T)[]'.

First, I don't really understand why typescript try to compare the type of argument array with a type (number | T)[] which does not exist in the different signatures. Does it try to compare the type of array with arguments type of comp function?

Of course I can replace the type of the argument array by any in the implementation signature, it works, but I would like to know if there is a better way to handle this case.

Upvotes: 0

Views: 580

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249706

The problem is that the implementation overload (ie. the last one) is not directly callable, so when you call the function recursively the types have to be compatible with one of the two overloads, and the union is not compatible with either (typescript will not try to combine signatures to get to allow unions to be passed in)

The simplest solution in such a situation is to duplicate the implementation signature:

export function select(
    array: Float32Array,
    first: number,
    nth: number,
    last: number,
    comp: (a: number, b: number) => boolean,
): void;
export function select<T>(
    array: T[],
    first: number,
    nth: number,
    last: number,
    comp: (a: T, b: T) => boolean,
): void;
export function select<T>(
    array: Float32Array | T[],
    first: number,
    nth: number,
    last: number,
    comp: (a: number | T, b: number | T) => boolean,
): void;
export function select<T>(
    array: Float32Array | T[],
    first: number,
    nth: number,
    last: number,
    comp: (a: number | T, b: number | T) => boolean,
): void {
    // Implementation of Floyd-Rivest algorithm
    // Some code
    let newFirst = 0
    let newLast = 0
    select(array, newFirst, nth, newLast, comp);
    // Some code
}

Another solution would be to use a type that is more general and is applicable for both types of array, as suggested in the comments.

Upvotes: 1

Related Questions