oxc-project/oxc · warning · OxcDiagnostic

A record is preferred over an index signature.

Error message

A record is preferred over an index signature.

What it means

Warning from typescript/consistent-indexed-object-style (crates/oxc_linter/src/rules/typescript/consistent_indexed_object_style.rs:36). With the default option 'record', types keyed by strings/numbers must use Record<K, V> utility syntax; it fires on index signatures like '{ [key: string]: unknown }' and suggests a Record alternative in the help text.

Source

Thrown at crates/oxc_linter/src/rules/typescript/consistent_indexed_object_style.rs:36

    rule::{DefaultRuleConfig, Rule},
};

fn consistent_indexed_object_style_diagnostic(
    preferred: ConsistentIndexedObjectStyleConfig,
    span: Span,
) -> OxcDiagnostic {
    let (warning_message, help_message) = match preferred {
        ConsistentIndexedObjectStyleConfig::Record => (
            "A record is preferred over an index signature.",
            "Use a record type such as `Record<string, unknown>` instead of an index signature.",
        ),
        ConsistentIndexedObjectStyleConfig::IndexSignature => (
            "An index signature is preferred over a record.",
            "Use an index signature such as `{ [key: string]: unknown }` instead of a record type.",
        ),
    };

    OxcDiagnostic::warn(warning_message).with_help(help_message).with_label(span)
}

#[derive(Debug, Clone, Default, Deserialize)]
pub struct ConsistentIndexedObjectStyle(ConsistentIndexedObjectStyleConfig);

#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "kebab-case")]
enum ConsistentIndexedObjectStyleConfig {
    /// When set to `record`, enforces the use of a `Record` for indexed object types, e.g. `Record<string, unknown>`.
    #[default]
    Record,
    /// When set to `index-signature`, enforces the use of indexed signature types, e.g. `{ [key: string]: unknown }`.
    IndexSignature,
}

declare_oxc_lint!(
    /// ### What it does
    ///

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Rewrite as 'Record<string, unknown>' / 'Record<number, Foo>'
  2. When the type also has named members, split it: keep the interface for named members and add a Record-typed intersection or an index property typed as Record
  3. If index signatures are the house style, set the option to 'index-signature' in .oxlintrc.json

Example fix

// before
type Dict = { [key: string]: unknown };

// after
type Dict = Record<string, unknown>;
Defensive patterns

Strategy: validation

Validate before calling

const INDEX_SIG = /\[\s*(key|k)?\s*:\s*(string|number|symbol)\s*\]\s*:/;
if (INDEX_SIG.test(source)) fail('index signature found; prefer Record<K, V>');

Type guard

function isRecordOf<V>(v: unknown): v is Record<string, V> {
  return typeof v === 'object' && v !== null;
}

Prevention

When it happens

Trigger: oxlint runs with the default (or explicit) 'indexedObjectStyle': 'record' and the file declares 'type Dict = { [key: string]: unknown };', a parameter typed '{ [k: number]: Foo }', or an interface with an index signature member.

Common situations: Enabling recommended presets over a codebase that predates Record; mixing styles after merging projects; interfaces that legitimately need both named members and an index signature (Record cannot express named members directly).

Related errors


AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20). Data as JSON: /api/errors/9bc865e36d488f28. Report an issue: GitHub.