oxc-project/oxc · warning · OxcDiagnostic
Require `for-in` loops to include an `if` statement
Error message
Require `for-in` loops to include an `if` statement
What it means
This is the `guard-for-in` lint rule (oxlint port of ESLint guard-for-in). It fires when a `for...in` loop's body is not wrapped in an `if` statement. The rationale is that for-in iterates inherited enumerable properties from the prototype chain, so unfiltered iteration can process unwanted keys; the rule is pedantic and off in ESLint's default sets.
Source
Thrown at crates/oxc_linter/src/rules/eslint/guard_for_in.rs:9
use oxc_ast::{AstKind, ast::Statement};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use crate::{AstNode, context::LintContext, rule::Rule};
fn guard_for_in_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Require `for-in` loops to include an `if` statement")
.with_help("The body of a for-in should be wrapped in an if statement to filter unwanted properties from the prototype.")
.with_label(span)
}
#[derive(Debug, Default, Clone)]
pub struct GuardForIn;
declare_oxc_lint!(
/// ### What it does
///
/// Require for-in loops to include an if statement.
///
/// ### Why is this bad?
///
/// Looping over objects with a `for in` loop will include properties that are inherited through
/// the prototype chain. Using a `for in` loop without filtering the results in the loop can
/// lead to unexpected items in your for loop which can then lead to unexpected behaviour.
///View on GitHub (pinned to e1e7af627c)
Solutions
- Replace the for-in with `for (const k of Object.keys(obj))` — this only iterates own enumerable keys and satisfies/obviates the rule.
- If keeping for-in, wrap the body in a guard such as `if (Object.prototype.hasOwnProperty.call(obj, k)) { ... }` or a domain filter `if (shouldProcess(k)) { ... }`.
- Disable the rule in `.oxlintrc.json` or with an inline `oxlint-disable guard-for-in` comment when the iterated object is known to be a null-prototype or trusted plain record.
Example fix
// before
for (const key in config) {
apply(key, config[key]);
}
// after
for (const key of Object.keys(config)) {
apply(key, config[key]);
} Defensive patterns
Strategy: validation
Validate before calling
// Prefer Object.keys/entries so prototype keys can never leak in:
for (const [key, value] of Object.entries(config)) { /* ... */ }
// CI gate: oxlint --rule guard_for_in src/ Type guard
// If for-in must stay, make own-key iteration explicit:
if (Object.prototype.hasOwnProperty.call(obj, key)) { /* own property only */ } Prevention
- Default to Object.keys/values/entries for plain-object iteration; reserve for-in for cases where prototype traversal is intended.
- Treat any new for-in in review as a question: which properties do you expect, including inherited ones?
- Freeze or create objects with Object.create(null) when they are used as dictionaries, removing prototype hazards entirely.
When it happens
Trigger: Any `for (const k in obj) { doSomething(k); }` whose direct body is not an `IfStatement`. The diagnostic is raised on every for-in statement the visitor finds where the loop body is not a single `if` (crates/oxc_linter/src/rules/eslint/guard_for_in.rs). There are no configuration options.
Common situations: Enabling pedantic/strict rule presets (e.g. migrating a config with `guard-for-in: error` to oxlint); legacy code that iterates plain objects with for-in; newly added for-in loops in a repo where the rule was already on; iterating over objects that have been extended or use prototypes/class instances.
Related errors
- Empty array binding pattern
- Empty object binding pattern
- Missing parameters.
- Missing radix parameter.
- Invalid radix parameter, must be an integer between 2 and 36
AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20).
Data as JSON: /api/errors/5740bb02964d78bc.
Report an issue: GitHub.