You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
deleteCount param to splice() is not optional in es5 types #32638
Unless I'm confused about something, the function's behavior is well-predictable when given one argument.
The spec is not authoritative as to argument optionality since it's not a well-defined concept in JS and ToInteger(undefined) gives 0 which is a good default argument in many cases.
For example Number.prototype.toFixed lists its argument as required, but TypeScript doesn't enforce this because (0).toFixed() is a well-formed and predictable result. Same for String.prototype.slice - its arguments are both listed as "required" but has predictable and expected behavior with one.
Hmm interesting. The reason I opened this is because we had a .splice(0), and then our app broke in an old version of opera because there the second argument defaulted to 0 (correctly for es5) instead of the array length (explicit in es6).
The issue is the default value essentially changed between es5 and es6.
Given the the default becomes something else in es6, and the param is not marked as optional in es5, do you think it makes sense for there to be an exception in this case?
It's not safe to omit deleteCount unless you know for sure what environment you are running in.
When the splice method is called with two or more arguments [...]
Based on this, and the fact that the behavior changed between ES5 and ES6 (despite "don't break the web" being the unofficial ES motto), the default value of 0 therefore seems to just be an accident of the integer coercion rules and not directly intended in the design. It definitely seems like the older spec wants to treat the second parameter as required, at any rate.
That said, even with the reasoning above, the fact that in practice there is a "default" and it isn't consistent between ES5 and ES6 is quite frustrating.
Yeah, after reading more, it seems like making the 2nd arg required for ES5 and optional for ES6 would be better in this particular case.
I don't think I've ever called splice without a second arg, though, because it's not very useful.
The ES6 spec explicitly mentions behaviour for 1 Arg being passed,
Else if the number of actual arguments is 1, then
Let insertCount be 0.
Let actualDeleteCount be len – actualStart
But it seems like the ES5 spec is hinting that if called with zero or one Arg, then the behavior is more or less undefined, even if the algorithm outlined defines a behavior
Thanks for the feedback on this BTW. When/if someone submits a PR it'd be nice if this issue had a clear and concrete description of what happens in ES5 vs ES6 with the various argument counts.
Hi! I've run into a related issue. I'm not sure if this is really a bug with TypeScript, the interpreters, or the spec itself. However, the typings are at the very least misleading. ES2022 deleteCount was clarified to be clearly optional, but if you pass undefined as deleteCount splice() will fail silently. I noticed this when I wrapped splice with another class I was working on, which should work according to the typings.
While deleteCount may or may not be optional (your guess is as good as mine), it cannot be undefined.
⏯ Playground Link
I added several cases to test behaviour at the following:
classFailingWrapper{arr=[1,2,3,4];splice(start:number,deleteCount?: number){// When deleteCount is 'undefined' method fails silentlyreturnthis.arr.splice(start,deleteCount);}}constwrapper=newFailingWrapper();constremoved=wrapper.splice(2)// deleteCount is undefinedconsole.log(`Case 4: Arr length is now ${wrapper.arr.length}, and removed length is ${removed.length}`);
🙁 Actual behavior
Case 4: Arr length is now 4, and removed length is 0
splice() failed silently here because undefined was passed as deleteCount.
🙂 Expected behavior
Case 4: Arr length is now 2, and removed length is 2
According to the typings splice() should have worked.
Addendum
It looks like the PR was reverted, but this edge case doesn't seem to be accounted for. Daniel Rosenwasser (@DanielRosenwasser) and Ryan Cavanaugh (@RyanCavanaugh) can you please take another look at this? It's easy enough to code around, but this is unexpected / undocumented behaviour, and was a serious pain to track down.
Ryan Cavanaugh (@RyanCavanaugh) how would that work? I just tried adding a splice(start: number): T[]; separate to splice(start: number, deleteCount: number): T[]; and it still allows undefined
To be clear, as noted above, when deleteCount is assigned undefined it defaults to 0. It's just weird because:
arr.splice(number)// deleteCount is length - startarr.splice(number,undefined)// deleteCount is 0
Behave totally different, when you'd expect they would be the same. I don't know if this should be handled by the transpiler, or simply just documented and warn users of weird behaviour of splice.
Even if it remains optional in ES5, could at least the documentation be clarified? The current documentation @param deleteCount The number of elements to remove. made me wonder what to expect. (Nothing is removed? One element is removed? All elements starting with start are removed?) MDN documents the ES6 behavior, without mentioning that the ES5 spec said it to be required, and that ES5-compliant browsers practically had a different default value. But even if MDN was 100% correct, having to leave the IDE takes time.
Activity
AnyhowStep commentedon Jul 31, 2019
ES5 Array.prototype.splice (start, deleteCount, ...
ToInteger
ToNumber
ToInteger
max(ToInteger(undefined),0)is justmax(+0, 0)is just zeroRyanCavanaugh commentedon Jul 31, 2019
Unless I'm confused about something, the function's behavior is well-predictable when given one argument.
The spec is not authoritative as to argument optionality since it's not a well-defined concept in JS and
ToInteger(undefined)gives0which is a good default argument in many cases.For example
Number.prototype.toFixedlists its argument as required, but TypeScript doesn't enforce this because(0).toFixed()is a well-formed and predictable result. Same forString.prototype.slice- its arguments are both listed as "required" but has predictable and expected behavior with one.tjenkinson commentedon Jul 31, 2019
Hmm interesting. The reason I opened this is because we had a
.splice(0), and then our app broke in an old version of opera because there the second argument defaulted to 0 (correctly for es5) instead of the array length (explicit in es6).The issue is the default value essentially changed between es5 and es6.
Given the the default becomes something else in es6, and the param is not marked as optional in es5, do you think it makes sense for there to be an exception in this case?
It's not safe to omit
deleteCountunless you know for sure what environment you are running in.fatcerberus commentedon Jul 31, 2019
The wording used in the spec is interesting:
Based on this, and the fact that the behavior changed between ES5 and ES6 (despite "don't break the web" being the unofficial ES motto), the default value of 0 therefore seems to just be an accident of the integer coercion rules and not directly intended in the design. It definitely seems like the older spec wants to treat the second parameter as required, at any rate.
That said, even with the reasoning above, the fact that in practice there is a "default" and it isn't consistent between ES5 and ES6 is quite frustrating.
AnyhowStep commentedon Jul 31, 2019
Yeah, after reading more, it seems like making the 2nd arg required for ES5 and optional for ES6 would be better in this particular case.
I don't think I've ever called splice without a second arg, though, because it's not very useful.
The ES6 spec explicitly mentions behaviour for 1 Arg being passed,
But it seems like the ES5 spec is hinting that if called with zero or one Arg, then the behavior is more or less undefined, even if the algorithm outlined defines a behavior
RyanCavanaugh commentedon Jul 31, 2019
OK, that sounds pretty annoying. Accepting PRs
RyanCavanaugh commentedon Jul 31, 2019
Thanks for the feedback on this BTW. When/if someone submits a PR it'd be nice if this issue had a clear and concrete description of what happens in ES5 vs ES6 with the various argument counts.
tjenkinson commentedon Jul 31, 2019
Awesome. I will put together a PR.
I know I have used
splice(0)several times in the past. It would have been great to have typescript flag it then :)AnyhowStep commentedon Jul 31, 2019
I think this is what happens.
Maybe someone can correct me if I made a mistake.
(0, 0)(start, 0)(0, array.length)(start, array.length-actualStart)*From this, the
actualStartis computed,make splice `deleteCount` required in es5.d.ts
make splice `deleteCount` required in es5.d.ts
tjenkinson commentedon Jul 31, 2019
PR: #32643
make splice `deleteCount` required in es5.d.ts (#32643)
buck-silver commentedon Nov 19, 2021
Hi! I've run into a related issue. I'm not sure if this is really a bug with TypeScript, the interpreters, or the spec itself. However, the typings are at the very least misleading. ES2022 deleteCount was clarified to be clearly optional, but if you pass
undefinedas deleteCount splice() will fail silently. I noticed this when I wrapped splice with another class I was working on, which should work according to the typings.While deleteCount may or may not be optional (your guess is as good as mine), it cannot be
undefined.⏯ Playground Link
I added several cases to test behaviour at the following:
Playground link with relevant code
💻 Code
🙁 Actual behavior
Case 4: Arr length is now 4, and removed length is 0splice() failed silently here because undefined was passed as deleteCount.
🙂 Expected behavior
Case 4: Arr length is now 2, and removed length is 2According to the typings splice() should have worked.
Addendum
It looks like the PR was reverted, but this edge case doesn't seem to be accounted for. Daniel Rosenwasser (@DanielRosenwasser) and Ryan Cavanaugh (@RyanCavanaugh) can you please take another look at this? It's easy enough to code around, but this is unexpected / undocumented behaviour, and was a serious pain to track down.
RyanCavanaugh commentedon Nov 19, 2021
That's annoying as heck. We'll have to make it two overloads instead of having an optional parameter.
tjenkinson commentedon Nov 19, 2021
Ryan Cavanaugh (@RyanCavanaugh) how would that work? I just tried adding a
splice(start: number): T[];separate tosplice(start: number, deleteCount: number): T[];and it still allowsundefinedbuck-silver commentedon Nov 19, 2021
To be clear, as noted above, when deleteCount is assigned undefined it defaults to 0. It's just weird because:
Behave totally different, when you'd expect they would be the same. I don't know if this should be handled by the transpiler, or simply just documented and warn users of weird behaviour of splice.
jtotht commentedon Jun 10, 2023
Even if it remains optional in ES5, could at least the documentation be clarified? The current documentation
@param deleteCountThe number of elements to remove. made me wonder what to expect. (Nothing is removed? One element is removed? All elements starting withstartare removed?) MDN documents the ES6 behavior, without mentioning that the ES5 spec said it to be required, and that ES5-compliant browsers practically had a different default value. But even if MDN was 100% correct, having to leave the IDE takes time.