Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ref: prefer for..of loops over indexed access #84307

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

TkDodo
Copy link
Contributor

@TkDodo TkDodo commented Jan 30, 2025

This PR refactors traditional for loops which use an indexed access to for..of loops, if the index was only used to access the item of the collection that was iterated over.

for..of loops plays better with noUncheckedIndexedAccess, as they avoids a no-null-assertion (!)

The prefer-for-of rule of typescript-eslint was also enabled.

Note: These changes should be mostly safe. There’s a slim chance that we were looping over a non-iterable, like an object that had a length property. In one case, the types disallowed for..of because the element is not an iterable, only an indexable. Iterating over a non-iterable will yield a runtime error.

IF we had something that was any on type level, or was asserted to be an array, when it was in fact an indexable at runtime, we would now get an error with for..of.

this plays better with `noUncheckedIndexedAccess`, as it avoids a no-null-assertion (!)
@github-actions github-actions bot added the Scope: Frontend Automatically applied to PRs that change frontend components label Jan 30, 2025
@TkDodo TkDodo marked this pull request as ready for review January 30, 2025 14:34
@TkDodo TkDodo requested review from a team as code owners January 30, 2025 14:34
@TkDodo TkDodo requested a review from ryan953 January 30, 2025 14:36
Copy link
Member

@gggritso gggritso left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹

Copy link
Member

@natemoo-re natemoo-re left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice cleanup! 🥔🥔🥔

@JonasBa
Copy link
Member

JonasBa commented Jan 30, 2025

We need keep profiling to still using the old for loop as that is very performance sensitive part of the codebase (I can fix it after the merge too). The reason is that for of loops have extra iterator overhead that might end up negatively impacting the flamegraph import, stack iteration and a lot of our rendering code which can easily run in the magnitude of 100k loops per second across the render pipeline and with the addition of continuous profiling, the likeliness that we'll end up working with even more in the future is even higher :/

I would also say that I'm generally not entirely on board with this (my philosophy is to just let people write code, as long it's safe) and I think this change might result in a slightly more subtle debugging experience at the expense of convenience.

For example, if you consider a user that is investigating a cannot read property x of undefined error for the following two cases, the good ol for loop nudges towards more runtime safety and the inconvenience of writing the check can be seen as a feature :)

for(let i = 0; ...){
  const item = arr[i]! // <- unsafe
  doSomething(item)
}

vs

for(item of arr){
  doSomething(item)
}

That said, the nice side benefit of this is that we will be warned about non iterables, which I think is a good improvement. I wanted to highlight the small change, but imo my concern isn't strong enough to warrant blocking this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Scope: Frontend Automatically applied to PRs that change frontend components
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants