Skip to content

Conversation

@ryansuhartanto
Copy link

@ryansuhartanto ryansuhartanto commented Dec 19, 2025

Summary

Added new option to lint/complexity/useLiteralKeys called noPropertyAccessFromIndexSignature.

Relevant issue & discussions:

I used Claude Opus 4.5 in Github Copilot to assist me in this PR. Specifically:

  • Understand the project structure
  • Provide guide on the toolings (just, cargo-insta, etc.)
  • Provide references from existing codebases

I manually wrote the code itself with real time clippy capability.

Test Plan

  1. Adds the following test files:

    • specs/complexity/useLiteralKeys/noPropertyAccessFromIndexSignature.ts
    • specs/complexity/useLiteralKeys/noPropertyAccessFromIndexSignature.options.json
  2. Run just test-lintrule useLiteralKeys

  3. Notice it created noPropertyAccessFromIndexSignature.ts.snap.new and removes the .new

  4. Run just ready which outputs the following error:

    Output
    error: public documentation for `set_double_text_expression` links to private item `TextExpressionKind::Double`
       --> crates/biome_html_parser/src/parser.rs:132:43
        |
    132 |     /// When `value` is `true`, enables [`TextExpressionKind::Double`].
        |                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^ this item is private
        |
        = note: this link will resolve properly if you pass `--document-private-items`
        = note: `-D rustdoc::private-intra-doc-links` implied by `-D warnings`
        = help: to override `-D warnings` add `#[allow(rustdoc::private_intra_doc_links)]`
    
    error: could not document `biome_html_parser`
    warning: build failed, waiting for other jobs to finish...
    error: Recipe `documentation` failed on line 143 with exit code 101
    error: Recipe `ready` failed on line 249 with exit code 101
    

    I believe this is outside of the scope of the PR, which I ignored.

Important

This actually made a new file invalid.snap.new that replaces every literal character \ with / for some reason.
I ignored this and thought it's just a bug in the development tooling.
Check spoiler below.

invalid.snap diff

This is the result of adding domains: &[RuleDomain::Project],.
Not sure if this a bug or intended behavior.

diff --git a/crates/biome_js_analyze/tests/specs/complexity/useLiteralKeys/invalid.js.snap b/crates/biome_js_analyze/tests/specs/complexity/useLiteralKeys/invalid.js.snap
index 2bdeafaa8..54f33b10d 100644
--- a/crates/biome_js_analyze/tests/specs/complexity/useLiteralKeys/invalid.js.snap
+++ b/crates/biome_js_analyze/tests/specs/complexity/useLiteralKeys/invalid.js.snap
@@ -1,5 +1,6 @@
 ---
 source: crates/biome_js_analyze/tests/spec_tests.rs
+assertion_line: 152
 expression: invalid.js
 ---
 # Input
@@ -39,18 +40,18 @@ a = {
 // optional chain
 a?.["b"]?.['c']?.d?.e?.["f"]
 a = {
-  ["line1\
+  ["line1/
   line2"]: true,
 };
 a = {
-  [`line1\
+  [`line1/
   line2`]: true,
 };
 a = {
-  ["line1\nline2"]: true,
+  ["line1/nline2"]: true,
 };
 a = {
-  [`line1\nline2`]: true,
+  [`line1/nline2`]: true,
 };
 a = {
   [0]: 0,
@@ -583,7 +584,7 @@ invalid.js:34:5 lint/complexity/useLiteralKeys  FIXABLE  ━━━━━━━
   > 34 │ a?.["b"]?.['c']?.d?.e?.["f"]
        │     ^^^
     35 │ a = {
-    36 │   ["line1\
+    36 │   ["line1/
   
   i Unsafe fix: Use a literal key instead.
   
@@ -601,7 +602,7 @@ invalid.js:34:12 lint/complexity/useLiteralKeys  FIXABLE  ━━━━━━━
   > 34 │ a?.["b"]?.['c']?.d?.e?.["f"]
        │            ^^^
     35 │ a = {
-    36 │   ["line1\
+    36 │   ["line1/
   
   i Unsafe fix: Use a literal key instead.
   
@@ -619,7 +620,7 @@ invalid.js:34:25 lint/complexity/useLiteralKeys  FIXABLE  ━━━━━━━
   > 34 │ a?.["b"]?.['c']?.d?.e?.["f"]
        │                         ^^^
     35 │ a = {
-    36 │   ["line1\
+    36 │   ["line1/
   
   i Unsafe fix: Use a literal key instead.
   
@@ -635,7 +636,7 @@ invalid.js:36:4 lint/complexity/useLiteralKeys  FIXABLE  ━━━━━━━
   
     34 │ a?.["b"]?.['c']?.d?.e?.["f"]
     35 │ a = {
-  > 36 │   ["line1\
+  > 36 │   ["line1/
        │    ^^^^^^^
   > 37 │   line2"]: true,
        │   ^^^^^^
@@ -646,9 +647,9 @@ invalid.js:36:4 lint/complexity/useLiteralKeys  FIXABLE  ━━━━━━━
   
     34 34 │   a?.["b"]?.['c']?.d?.e?.["f"]
     35 35 │   a = {
-    36    │ - ··["line1\
+    36    │ - ··["line1/
     37    │ - ··line2"]:·true,
-       36 │ + ··"line1\
+       36 │ + ··"line1/
        37 │ + ··line2":·true,
     38 38 │   };
     39 39 │   a = {
@@ -663,7 +664,7 @@ invalid.js:40:4 lint/complexity/useLiteralKeys  FIXABLE  ━━━━━━━
   
     38 │ };
     39 │ a = {
-  > 40 │   [`line1\
+  > 40 │   [`line1/
        │    ^^^^^^^
   > 41 │   line2`]: true,
        │   ^^^^^^
@@ -674,9 +675,9 @@ invalid.js:40:4 lint/complexity/useLiteralKeys  FIXABLE  ━━━━━━━
   
     38 38 │   };
     39 39 │   a = {
-    40    │ - ··[`line1\
+    40    │ - ··[`line1/
     41    │ - ··line2`]:·true,
-       40 │ + ··"line1\
+       40 │ + ··"line1/
        41 │ + ··line2":·true,
     42 42 │   };
     43 43 │   a = {
@@ -691,14 +692,14 @@ invalid.js:44:4 lint/complexity/useLiteralKeys  FIXABLE  ━━━━━━━
   
     42 │ };
     43 │ a = {
-  > 44 │   ["line1\nline2"]: true,
+  > 44 │   ["line1/nline2"]: true,
        │    ^^^^^^^^^^^^^^
     45 │ };
     46 │ a = {
   
   i Unsafe fix: Use a literal key instead.
   
-    44 │ ··["line1\nline2"]:·true,
+    44 │ ··["line1/nline2"]:·true,
        │   -              -       
 
@@ -710,7 +711,7 @@ invalid.js:47:4 lint/complexity/useLiteralKeys  FIXABLE  ━━━━━━━
   
     45 │ };
     46 │ a = {
-  > 47 │   [`line1\nline2`]: true,
+  > 47 │   [`line1/nline2`]: true,
        │    ^^^^^^^^^^^^^^
     48 │ };
     49 │ a = {
@@ -719,8 +720,8 @@ invalid.js:47:4 lint/complexity/useLiteralKeys  FIXABLE  ━━━━━━━
   
     45 45 │   };
     46 46 │   a = {
-    47    │ - ··[`line1\nline2`]:·true,
-       47 │ + ··"line1\nline2":·true,
+    47    │ - ··[`line1/nline2`]:·true,
+       47 │ + ··"line1/nline2":·true,
     48 48 │   };
     49 49 │   a = {
   

Docs

To do.

@changeset-bot
Copy link

changeset-bot bot commented Dec 19, 2025

🦋 Changeset detected

Latest commit: 35919fa

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 14 packages
Name Type
@biomejs/biome Minor
@biomejs/cli-win32-x64 Minor
@biomejs/cli-win32-arm64 Minor
@biomejs/cli-darwin-x64 Minor
@biomejs/cli-darwin-arm64 Minor
@biomejs/cli-linux-x64 Minor
@biomejs/cli-linux-arm64 Minor
@biomejs/cli-linux-x64-musl Minor
@biomejs/cli-linux-arm64-musl Minor
@biomejs/wasm-web Minor
@biomejs/wasm-bundler Minor
@biomejs/wasm-nodejs Minor
@biomejs/backend-jsonrpc Patch
@biomejs/js-api Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions bot added A-Project Area: project A-Linter Area: linter L-JavaScript Language: JavaScript and super languages labels Dec 19, 2025
@ryansuhartanto
Copy link
Author

ryansuhartanto commented Dec 19, 2025

@arendjr I made the implementation to be backward compatible and it's quite simple, though it adds domains: &[RuleDomain::Project],. It essentially ignores obj["prop"] if it's coming from an index signature.

I actually plan this to be breaking (like modifying diagnosis and action) and actually check the noPropertyAccessFromIndexSignature from the tsconfig.json itself, but was halted by my limited understanding and time to the project.

Details

And the fact that my computer is being eaten alive by clippy and trying to compile tests with ~30GB of cargo caches.

Though the option name should be ignorePropertyAccessFromIndexSignature if this is good enough.

Let me know!

@arendjr
Copy link
Contributor

arendjr commented Dec 19, 2025

Few notes while I’m in the train:

  • adding an existing (non-nursery) rule to the project domain may be a controversial choice, since some users that rely on the rule may wish to exclude the domain due to performance considerations. not sure if there’s consensus on this.
  • if you plan breaking changes anyway, maybe we need to consider adding a new “more advanced” version of the rule instead. we don’t really have a precedent for that though….

@ryansuhartanto
Copy link
Author

adding an existing (non-nursery) rule to the project domain

Great catch, the LLM told me something about adding it to gain access to the ModuleResolver.

if you plan breaking changes anyway, maybe we need to consider adding a new “more advanced” version of the rule instead. we don’t really have a precedent for that though…

I plan to make this PR backwards compatible, users are expected to use the rule as is and won't notice a difference unless they opt-in to the noPropertyAccessFromIndexSignature (or ignorePropertyAccessFromIndexSignature whatever).

I'll happily push more code (removing the RuleDomain::Project and testing if ModuleResolver still works afterward) after I get some shut-eye. Thanks for the reply!

@lgarron
Copy link
Contributor

lgarron commented Dec 20, 2025

Any reason this shouldn't read from tsconfig.json, given that Biome already has tooling for that?

@ryansuhartanto
Copy link
Author

ryansuhartanto commented Dec 21, 2025

It seems that ModuleResolver is only available when the rule uses domains: &[RuleDomain::Project],. Which does actually make this rule quite complicated.

Any reason this shouldn't read from tsconfig.json, given that Biome already has tooling for that?

I'm still learning about the tooling of this project and hopefully I can implement this later on.

But the core issue I'm having is whether to continue this PR or not since the complexity of the rule itself. Is there a way to introduce a new project rule that can override existing rule diagnostic & action? @arendjr @lgarron

I actually need some discussion due to the scope of this. Based on my research, this is the best way to introduce the project domain to be aware of noPropertyAccessFromIndexSignature TypeScript feature.

I hope by checking no_property_access_from_index_signature is enabled/not would make it 100% backward compatible and that the user have a nice feature of the rule not contradicting the Typescript linter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Linter Area: linter A-Project Area: project L-JavaScript Language: JavaScript and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants