In a React component I have something similar to:
if (props.someArray){ // typecheck because someArray is of type 'things[] | undefined'
props.someArray.forEach((element, i) => {
someFunction(i, element, props.someArray); // error despite typecheck (due to "| undefined")
});
}
// The functions someArray argument's type is only 'things[]', not 'things[] | undefined'
const someFunction = (i: number, element: thing, someArray: things[]) => {
someArray[i] = // do some stuff;
}
Since the typecheck is already done before the loop, is it really necessary to do it again before calling the function inside the loop? (inconveniently checking on every iteration instead of just once before looping).
if (props.someArray){ // <- I'd like to keep just this
props.someArray.forEach((element) => {
if (props.someArray) { // <- is this really necessary?
someFunction(element, props.someArray);
}
});
}
CodePudding user response:
Array.prototype.forEach
uses a closure (i.e. an entirely separatefunction
) for the loop body, and TypeScript is cautious about how type-information passes between function boundaries.- Namely because TypeScript has no way of knowing how
forEach
will invoke the callback because any script can simply overwrite or replace the defaultArray.prototype.forEach
implementation and do something silly, like repeatedly invoke the function on only the first element or something.
- Namely because TypeScript has no way of knowing how
If you use
for(of)
instead then you won't have any problems because it doesn't use a closure: as it's a native language construct the TypeScript type engine (is that a word?) can reason about it more than it can with.forEach
.Other reasons to use
for(of)
instead offorEach
:- You can use
await
insidefor(of)
. - You can use
break
to abort a loop (you can't abort aforEach
without usingthrow
). - It works with any iterable, whereas
forEach
is only available on certain specific collection types (Array
,NodeList
, and a few others).
- You can use
So change your code to this:
if ( props.someArray ) {
for( const el of props.someArray ) {
someFunction( el, props.someArray );
}
}
If you want to get both the array-element and its index inside the for
loop then use entries()
like so:
if ( props.someArray ) {
for( const [idx, el] of props.someArray.entries() ) {
someFunction( idx, el, props.someArray );
}
}