Suggestion
π Search Terms
in:title array shift
#13778
β
Viability Checklist
My suggestion meets these guidelines:
β Suggestion
Whether an array element is defined/undefined is inconsistent. If you use direct access it's always defined even when the control flow clearly indicates it couldn't possibly be; if you use method-based access it's always possibly undefined even when the control flow clearly indicates it must be defined.
The discussion for random access in #13778 ended in keeping it defined by default and adding a pedantic compiler flag for making it all undefined. I'm proposing an extension of that to shift and other Array methods that should, for consistency, follow the same behaviour.
π Motivating Example
https://www.typescriptlang.org/play?#code/MYewdgzgLgBAZiEMC8MDaBGANAJiwZiwBYsBWAXQChRJYAjAQwCcUYAKAfQC4YwBXALZ0ApkwCUKAHwwOlauGgwAHqwQgAdBAAWASzhQ2EgPRHlMHRBh8wAE2FwdYYTZjCAbsLAwoWkHwDmWvCIMFoMlsIANsICnlCWdg5OLgx0IB6UjExsSsamUACeAA7CrkxMIExyNIoFqohoAJyN5DAmMHUWMImOzq4eXj5+gcFINiDClmAgsGEe3mGwAgxgdVExcRCZzGwFefC9cnrsaurRYP4+MNIADBIA3pQwzzA1sABe9Rraegb7n11rD1kv1PAthkEAO6lABWfEUwC0wmAAGs+qdzpcgrcni8smx3vtCiUyhUqgBfI5wdgAQjYGM8WLEDzapkqozOjKuyB5MBuMEhi3congkRADHiHT8LDSEtxzzeME+qDUTRarKV5gS9l6LmFg18AShsPhsERyLRLh0Zr8kRsYAA5LAiiAIBAdHRInURPKYPjCRqksJKJSgA
constfoo=[1,2,3,4,5]constbar=(_: number)=>_constx=foo.shift()// x is undefined even though foo has elements defined abovebar(x)// type errorconsty=foo[99]// y is defined even though foo does not have that many elementsbar(y)// fineif(foo.length>0){constz=foo.shift()// z is undefined even though we just checked foo.length > 0bar(z)// type error}if(!(foo.length)){// or foo.length === 0 whatever floats your boatconstz=foo[99]// z is defined even though we just checked it couldn't possibly bebar(z)// fine}π» Use Case
I can understand that it always could be undefined, e.g. in this case:
constx: number[]=[1,2,3];(async()=>{awaitlongAsyncTask();while(x.length>0)x.shift()})();(async()=>{awaitlongAsyncTask();console.log(x.shift())// could be undefined if the previous async block executes first})()However, this is a rarer case than x[n] which is much more commonly undefined. Given that x[n] is by default assumed defined unless the --noUncheckedIndexedAccess compiler flag is provided, .shift() should follow the same pattern.
Currently we use .shift() and a queue-based structure rather than random access, but TypeScript actually penalizes this. The only ways around this are assertions ! or adding an extra call for random access.
Workaround 1 (!):
constfoo=[1,2,3,4,5]constbar=(n: number)=>nbar(foo[99])// somehow finebar(foo.shift())// error?bar(foo.shift()!)// workaround
Consider the following case as why this is a problematic anti-pattern:
constfoo=myArray.map(myFunc)// I write this thinking the signature of foo is number[]// however due to a bug in myFunc, signature is actually (number | undefined)[]bar(foo.shift()!)// I type this as my workaround to behaviour described above// now no type checking occurs above and I have a much more obscure bug in my code!
Workaround 2 is more type-safe (extra call for random access):
However, this is an anti-pattern at best.
Suggestion
π Search Terms
in:title array shift
#13778
β Viability Checklist
My suggestion meets these guidelines:
β Suggestion
Whether an array element is defined/undefined is inconsistent. If you use direct access it's always defined even when the control flow clearly indicates it couldn't possibly be; if you use method-based access it's always possibly undefined even when the control flow clearly indicates it must be defined.
The discussion for random access in #13778 ended in keeping it
definedby default and adding a pedantic compiler flag for making it allundefined. I'm proposing an extension of that toshiftand otherArraymethods that should, for consistency, follow the same behaviour.π Motivating Example
https://www.typescriptlang.org/play?#code/MYewdgzgLgBAZiEMC8MDaBGANAJiwZiwBYsBWAXQChRJYAjAQwCcUYAKAfQC4YwBXALZ0ApkwCUKAHwwOlauGgwAHqwQgAdBAAWASzhQ2EgPRHlMHRBh8wAE2FwdYYTZjCAbsLAwoWkHwDmWvCIMFoMlsIANsICnlCWdg5OLgx0IB6UjExsSsamUACeAA7CrkxMIExyNIoFqohoAJyN5DAmMHUWMImOzq4eXj5+gcFINiDClmAgsGEe3mGwAgxgdVExcRCZzGwFefC9cnrsaurRYP4+MNIADBIA3pQwzzA1sABe9Rraegb7n11rD1kv1PAthkEAO6lABWfEUwC0wmAAGs+qdzpcgrcni8smx3vtCiUyhUqgBfI5wdgAQjYGM8WLEDzapkqozOjKuyB5MBuMEhi3congkRADHiHT8LDSEtxzzeME+qDUTRarKV5gS9l6LmFg18AShsPhsERyLRLh0Zr8kRsYAA5LAiiAIBAdHRInURPKYPjCRqksJKJSgA
π» Use Case
I can understand that it always could be
undefined, e.g. in this case:However, this is a rarer case than
x[n]which is much more commonlyundefined. Given thatx[n]is by default assumeddefinedunless the--noUncheckedIndexedAccesscompiler flag is provided,.shift()should follow the same pattern.Currently we use
.shift()and a queue-based structure rather than random access, but TypeScript actually penalizes this. The only ways around this are assertions!or adding an extra call for random access.Workaround 1 (
!):Consider the following case as why this is a problematic anti-pattern:
Workaround 2 is more type-safe (extra call for random access):
However, this is an anti-pattern at best.