Skip to content

linter: react/no-array-index-key false positive when index is passed as argument to a helper function #20939

Description

@ahmedelgabri

What version of Oxlint are you using?

1.58.0

What command did you run?

oxlint -c oxlint.json test.tsx

What does your .oxlintrc.json (or oxlint.config.ts) config file look like?

{
  "plugins": ["react"],
  "rules": {
    "react/no-array-index-key": "error"
  }
}

What happened?

react/no-array-index-key flags array index usage inside helper function arguments like key={getKey(item.id, index)}. ESLint only flags direct usage like key={index}, key={1 + index}, or key={`abc${index}`}.

Minimal reproduction (playground)

test.tsx:

declare function getKey(id: number, index: number): string

function List({items}: {items: {id: string; text: string}[]}) {
	return (
		<ul>
			{items.map((item, index) => (
				<li key={getKey(item.id, index)}>{item.text}</li>
			))}
		</ul>
	)
}

Expected: No error. The index is passed to a helper that combines it with a unique ID, not used directly as the key.

Actual:

x eslint-plugin-react(no-array-index-key): Usage of Array index in keys is not allowed
  ,-[test.tsx:9:9]
8 |             {items.map((item, index) => (
9 |                 <li key={getKey(item.id, index)}>{item.text}</li>
  :                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Root cause

span_contains_symbol_reference in crates/oxc_linter/src/rules/react/no_array_index_key.rs:121-127 checks if the index symbol reference appears anywhere within the key expression span:

fn span_contains_symbol_reference(ctx: &LintContext, symbol_id: SymbolId, span: Span) -> bool {
    ctx.scoping().get_resolved_reference_ids(symbol_id).iter().any(|&reference_id| {
        let reference = ctx.scoping().get_reference(reference_id);
        let reference_span = ctx.nodes().get_node(reference.node_id()).span();
        span.contains_inclusive(reference_span)
    })
}

Because index appears as an argument inside getKey(item.id, index), which is within the key attribute's expression span, the rule fires. ESLint's implementation checks whether the index is the direct value of the key prop or a direct operand of a simple binary/template expression, not whether it appears anywhere in a nested call expression.

ESLint comparison

Both ESLint 7.23.0 and ESLint 10.1.0 (with [email protected]) pass on this code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Fields

    Priority

    None yet

    Effort

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions