8000 Rule proposal: Prefer array.at(...) over array[...] as T | undefined · Issue #8979 · typescript-eslint/typescript-eslint · GitHub
[go: up one dir, main page]

Skip to content

Rule proposal: Prefer array.at(...) over array[...] as T | undefined #8979

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

Closed
6 tasks done
JoshuaKGoldberg opened this issue Apr 23, 2024 · 16 comments
Closed
6 tasks done
Labels
enhancement: new plugin rule New rule request for eslint-plugin locked due to age Please open a new issue if you'd like to say more. See https://typescript-eslint.io/contributing. package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin wontfix This will not be worked on

Comments

@JoshuaKGoldberg
Copy link
Member
JoshuaKGoldberg commented Apr 23, 2024

Before You File a Proposal Please Confirm You Have Done The Following...

My proposal is suitable for this project

  • My proposal specifically checks TypeScript syntax, or it proposes a check that requires type information to be accurate.
  • My proposal is not a "formatting rule"; meaning it does not just enforce how code is formatted (whitespace, brace placement, etc).
  • I believe my proposal would be useful to the broader TypeScript community (meaning it is not a niche proposal).

Description

Array index lookups aren't type-safe without the often-overly-strict noUncheckedIndexedAccess: they give back T, not T | undefined. A lot of TS code that predates Array.prototype.at() had to look like [index] as T | undefined instead of .at(index). Example in our repo:

const nextOperand = chain[index + 1] as ValidOperand | undefined;

Now that we have .at(), I think it makes sense to enforce that: if an array index lookup uses a type assertion to add | undefined, it should use .at instead.

Fail Cases

declare const values: string[];
declare const index: number;

values[index] as string | undefined;

Pass Cases

declare const values: string[];
declare const index: number;

values.at(index);

Additional Info

This is vaguely familiar to the idea of @typescript-eslint/no-unnecessary-type-assertion. But it includes a runtime change.

@JoshuaKGoldberg JoshuaKGoldberg added package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin triage Waiting for team members to take a look enhancement: new plugin rule New rule request for eslint-plugin labels Apr 23, 2024
@kirkwaiblinger
Copy link
Member

big +1 ❤️

  • Should this also have have any options around prohibiting .at(i)!, or preferring [i] or .at(i) in general? I'm thinking not since .at(i)! is much more useful (e.g. with negative indices), and changing to [i] is a bigger, less safe logic change than [i] as T | undefined to .at(i). And as you noted, noUncheckedIndexAccess, already exists.

  • Due to the runtime changes, let's be sure to provide a suggested fix, and not an autofix?

@bradzacher
Copy link
Member

We didn't used to use this cos we supported old nodejs versions!
Up until our v7 release our min was just v16.0.0 - but .at was only added in v16.6.0
But now that our min is v18 we can safely use this everywhere!

@bradzacher bradzacher added accepting prs Go ahead, send a pull request that resolves this issue and removed triage Waiting for team members to take a look labels Apr 24, 2024
kirkwaiblinger added a commit to kirkwaiblinger/typescript-eslint that referenced this issue May 3, 2024
@ronami
Copy link
Member
ronami commented Nov 7, 2024

Some thoughts:

Would it make sense to generalize it to prefer array.at(...) over array[...] when the contextual type is T | undefined?

Should this also fail too?

declare const values: string[];
declare const index: number;

const a: string | undefined = values[index];

Also regarding other contextual types of T | undefined:

declare const values: string[];
declare const index: number;

// explicit return type annotation
function foo(): string | undefined {
  return values[index];
}

// function argument
bar(values[index]);

function bar(s: string | undefined) {}

@OlivierZal
Copy link
Contributor
OlivierZal commented Nov 7, 2024

Couldn't there be some conflict with prefer-destructuring?

Since both options are not related a priori, someone would want both so that:

const item = array[0]

could be fixed either with:

const [item] = array

or:

const item = array.at(0)

Maybe could it be handled through an excludeZero option?

@bradzacher
Copy link
Member

@OlivierZal no because const [e] = arr is unsafe - it does not encode the possible undefined.

I would expect that the eventual implementation should flag the destructuring code and suggest using .at

@Josh-Cena
Copy link
Member

no because const [e] = arr is unsafe - it does not encode the possible undefined.

I would expect that the eventual implementation should flag the destructuring code and suggest using .at

I don't think it would, in the same sence that const x = arr[0] continues to be allowed with this rule. Only const x: string | undefined = arr[0] is flagged.

@bradzacher
Copy link
Member

Heh okay. I only quickly glanced at the top and missed that it requires annotation.
This by itself isnt a super useful rule - it's very niche as it is - I'd probably want a more broadly applicable rule to use .at everywhere.

@Josh-Cena
Copy link
Member

noUncheckedIndexedAccess by itself is a very contentious option since you are essentially shutting out all C-style for loops. This lint rule, without the contextual type, is marginally better than forcing people to turn on NUIA: it only forces you to use a less optimized alternative and creates a (IMO unnecessary) distinction between arrays and index signatures.

@bradzacher
Copy link
Member
bradzacher commented Nov 8, 2024

If we're just targetting arr[idx] as T | undefined then I would say that this rule is too niche to be something we should move to implement, personally.

Quickly searching via sourcegraph returns LESS THAN 600 RESULTS.

With that low volume IMO it distinctly fails the final check:

I believe my proposal would be useful to the broader TypeScript community (meaning it is not a niche proposal).

Personally I would raise a motion to close this rule proposal as too niche.

@Josh-Cena
Copy link
Member

You are searching for indexing with a constant, but most indexing happens with a variable. Adding that gives you 1.7k—idk if that's better.

@bradzacher
Copy link
Member

Frame it this way:

If we turned on this rule across the entirety of the public GitHub TypeScript code -- likely billions of lines of code -- there would be just 1.7k reports for this rule.

I ran the grep (with \w+ instead of \d+ at your suggestion) on our internal codebase at Canva -- 90k TS files and there was just 12 results. 10 of those results are for object keys. So there was just 2 hits.

Seems like it's niche enough that the cost:value proposition just isn't there.

@kirkwaiblinger
Copy link
Member

eh, yeah, i'm ok with this being closed

@bradzacher
Copy link
Member

@JoshuaKGoldberg @Josh-Cena thoughts?

@JoshuaKGoldberg
Copy link
Member Author

Yeah I'd prefer it to exist but don't feel passionately. The lack of applicable code or public excitement tampered my enthusiasm.

@Josh-Cena
Copy link
Member

No strong feelings from me

@JoshuaKGoldberg
Copy link
Member Author

rip

@JoshuaKGoldberg JoshuaKGoldberg closed this as not planned Won't fix, can't repro, duplicate, stale Nov 9, 2024
@JoshuaKGoldberg JoshuaKGoldberg added wontfix This will not be worked on and removed accepting prs Go ahead, send a pull request that resolves this issue labels Nov 9, 2024
@github-actions github-actions bot added the locked due to age Please open a new issue if you'd like to say more. See https://typescript-eslint.io/contributing. label Nov 17, 2024
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Nov 17, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement: new plugin rule New rule request for eslint-plugin locked due to age Please open a new issue if you'd like to say more. See https://typescript-eslint.io/contributing. package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin wontfix This will not be worked on
Projects
None yet
Development

No branches or pull requests

6 participants
0