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.
typescript generics recursion overloading
add a comment |
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.
typescript generics recursion overloading
What if you changeT
toArrayLike<T>
? Since you also allow aFloat32Array
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 addArrayLike<T> & Iterable<T>
.
– Tim Lundqvist
Nov 14 '18 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).
– SpacePotatoes
Nov 14 '18 at 14:46
add a comment |
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.
typescript generics recursion overloading
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.
typescript generics recursion overloading
typescript generics recursion overloading
asked Nov 14 '18 at 14:19
SpacePotatoesSpacePotatoes
11
11
What if you changeT
toArrayLike<T>
? Since you also allow aFloat32Array
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 addArrayLike<T> & Iterable<T>
.
– Tim Lundqvist
Nov 14 '18 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).
– SpacePotatoes
Nov 14 '18 at 14:46
add a comment |
What if you changeT
toArrayLike<T>
? Since you also allow aFloat32Array
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 addArrayLike<T> & Iterable<T>
.
– Tim Lundqvist
Nov 14 '18 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).
– SpacePotatoes
Nov 14 '18 at 14:46
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>
.– Tim Lundqvist
Nov 14 '18 at 14:28
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>
.– Tim Lundqvist
Nov 14 '18 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).
– SpacePotatoes
Nov 14 '18 at 14:46
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).
– SpacePotatoes
Nov 14 '18 at 14:46
add a comment |
1 Answer
1
active
oldest
votes
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.
Doesn't adding a union signature basically defeat the purpose of the overloads?
– Aaron
Nov 14 '18 at 16:24
Not necessarily .. the first overload will be active forFloatArrays
so the callback will takenumbers
. For otherT
s the callback will takeT
. Only if it's a union of typeFloatArray | T
will the last overload be triggered
– Titian Cernicova-Dragomir
Nov 14 '18 at 16:33
But it seems like you can basically pass anything that satisfies the union and it'll compile, even though it could be an error... likeselect(floatArray, 1, 2, 3, (a: string, b: string) => a == b)
is allowed because itfloatArray
matchesFloat32Array
and(string, string) => boolean
matches(T, T) => boolean
... even though it no longer makes sense.
– Aaron
Nov 14 '18 at 16:40
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53302360%2foverloading-a-recursive-function-in-typescript-with-array-and-typedarray%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
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.
Doesn't adding a union signature basically defeat the purpose of the overloads?
– Aaron
Nov 14 '18 at 16:24
Not necessarily .. the first overload will be active forFloatArrays
so the callback will takenumbers
. For otherT
s the callback will takeT
. Only if it's a union of typeFloatArray | T
will the last overload be triggered
– Titian Cernicova-Dragomir
Nov 14 '18 at 16:33
But it seems like you can basically pass anything that satisfies the union and it'll compile, even though it could be an error... likeselect(floatArray, 1, 2, 3, (a: string, b: string) => a == b)
is allowed because itfloatArray
matchesFloat32Array
and(string, string) => boolean
matches(T, T) => boolean
... even though it no longer makes sense.
– Aaron
Nov 14 '18 at 16:40
add a comment |
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.
Doesn't adding a union signature basically defeat the purpose of the overloads?
– Aaron
Nov 14 '18 at 16:24
Not necessarily .. the first overload will be active forFloatArrays
so the callback will takenumbers
. For otherT
s the callback will takeT
. Only if it's a union of typeFloatArray | T
will the last overload be triggered
– Titian Cernicova-Dragomir
Nov 14 '18 at 16:33
But it seems like you can basically pass anything that satisfies the union and it'll compile, even though it could be an error... likeselect(floatArray, 1, 2, 3, (a: string, b: string) => a == b)
is allowed because itfloatArray
matchesFloat32Array
and(string, string) => boolean
matches(T, T) => boolean
... even though it no longer makes sense.
– Aaron
Nov 14 '18 at 16:40
add a comment |
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.
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.
answered Nov 14 '18 at 15:45
Titian Cernicova-DragomirTitian Cernicova-Dragomir
65.7k34361
65.7k34361
Doesn't adding a union signature basically defeat the purpose of the overloads?
– Aaron
Nov 14 '18 at 16:24
Not necessarily .. the first overload will be active forFloatArrays
so the callback will takenumbers
. For otherT
s the callback will takeT
. Only if it's a union of typeFloatArray | T
will the last overload be triggered
– Titian Cernicova-Dragomir
Nov 14 '18 at 16:33
But it seems like you can basically pass anything that satisfies the union and it'll compile, even though it could be an error... likeselect(floatArray, 1, 2, 3, (a: string, b: string) => a == b)
is allowed because itfloatArray
matchesFloat32Array
and(string, string) => boolean
matches(T, T) => boolean
... even though it no longer makes sense.
– Aaron
Nov 14 '18 at 16:40
add a comment |
Doesn't adding a union signature basically defeat the purpose of the overloads?
– Aaron
Nov 14 '18 at 16:24
Not necessarily .. the first overload will be active forFloatArrays
so the callback will takenumbers
. For otherT
s the callback will takeT
. Only if it's a union of typeFloatArray | T
will the last overload be triggered
– Titian Cernicova-Dragomir
Nov 14 '18 at 16:33
But it seems like you can basically pass anything that satisfies the union and it'll compile, even though it could be an error... likeselect(floatArray, 1, 2, 3, (a: string, b: string) => a == b)
is allowed because itfloatArray
matchesFloat32Array
and(string, string) => boolean
matches(T, T) => boolean
... even though it no longer makes sense.
– Aaron
Nov 14 '18 at 16:40
Doesn't adding a union signature basically defeat the purpose of the overloads?
– Aaron
Nov 14 '18 at 16:24
Doesn't adding a union signature basically defeat the purpose of the overloads?
– Aaron
Nov 14 '18 at 16:24
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– Titian Cernicova-Dragomir
Nov 14 '18 at 16:33
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– Titian Cernicova-Dragomir
Nov 14 '18 at 16:33
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.– Aaron
Nov 14 '18 at 16:40
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.– Aaron
Nov 14 '18 at 16:40
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53302360%2foverloading-a-recursive-function-in-typescript-with-array-and-typedarray%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
What if you change
T
toArrayLike<T>
? Since you also allow aFloat32Array
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 addArrayLike<T> & Iterable<T>
.– Tim Lundqvist
Nov 14 '18 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).
– SpacePotatoes
Nov 14 '18 at 14:46