0

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.

2
  • What if you change T[] to ArrayLike<T>? Since you also allow a Float32Array you will not be using any of the Array prototype function. If you also use it in a for-of loop or something you may add ArrayLike<T> & Iterable<T>. Commented Nov 14, 2018 at 14:28
  • Thanks, I didn't know about these types in Typescript. Iterable<T> seems to work (and therefore, no overload is required). I can't use ArrayLike which seems to be readonly (the array is mutaded in the implementation). Commented Nov 14, 2018 at 14:46

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Doesn't adding a union signature basically defeat the purpose of the overloads?
Not necessarily .. the first overload will be active for FloatArrays so the callback will take numbers. For other T[]s the callback will take T. Only if it's a union of type FloatArray | T[] will the last overload be triggered
But it seems like you can basically pass anything that satisfies the union and it'll compile, even though it could be an error... like select(floatArray, 1, 2, 3, (a: string, b: string) => a == b) is allowed because it floatArray matches Float32Array and (string, string) => boolean matches (T, T) => boolean... even though it no longer makes sense.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.