oxc-project/oxc · warning · OxcDiagnostic

Do not use an empty interface declaration.

Error message

Do not use an empty interface declaration.

What it means

Diagnostic from oxlint's port of typescript-eslint's no-empty-object-type rule. An interface with no members (interface Foo {}) behaves like the {} type and accepts any non-nullish value, so the rule bans it to prevent silent loss of type safety; the help text at crates/oxc_linter/src/rules/typescript/no_empty_object_type.rs:19 states exactly this. It fires when the rule visits an empty TSInterfaceDeclaration that the config (allowInterfaces, allowWithName) does not exempt.

Source

Thrown at crates/oxc_linter/src/rules/typescript/no_empty_object_type.rs:27

use oxc_macros::declare_oxc_lint;
use oxc_semantic::NodeId;
use oxc_span::Span;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::{
    AstNode,
    context::LintContext,
    fixer::RuleFixer,
    rule::{DefaultRuleConfig, Rule},
    utils::deserialize_regex_option,
};

fn no_empty_object_type_diagnostic<S: Into<Cow<'static, str>>>(
    span: Span,
    message: S,
) -> OxcDiagnostic {
    OxcDiagnostic::warn(message)
        .with_help("To avoid confusion around the {} type allowing any non-nullish value, this rule bans usage of the {} type.")
        .with_label(span)
}

#[derive(Debug, Default, Clone)]
pub struct NoEmptyObjectType(Box<NoEmptyObjectTypeConfig>);

#[expect(clippy::struct_field_names)]
#[derive(Debug, Default, Clone, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", default, deny_unknown_fields)]
pub struct NoEmptyObjectTypeConfig {
    /// Whether to allow empty interfaces.
    allow_interfaces: AllowInterfaces,
    /// Whether to allow empty object type literals.
    allow_object_types: AllowObjectTypes,
    /// A stringified regular expression to allow interfaces and object type aliases with the configured name.
    ///
    /// This can be useful if your existing code style includes a pattern of declaring empty types with `{}` instead of `object`.

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Add at least one real member to the interface
  2. Replace it with a type alias that models the intended shape
  3. If you truly mean 'exactly the empty object', use `type Foo = Record<string, never>`
  4. Configure allowInterfaces or allowWithName in oxlintrc when the empty interface is intentional
  5. Suppress once with `// oxlint-disable-next-line typescript/no-empty-object-type`

Example fix

// before
interface User {}

// after
interface User {
  id: string;
}
Defensive patterns

Strategy: validation

Validate before calling

# .github/workflows/lint.yml
- run: npx oxlint --deny-warnings .

# or configure the exemption up front in oxlintrc.json
{
  "rules": {
    "typescript/no-empty-object-type": ["error", { "allowWithName": "^(?:I|Props|Params)$" }]
  }
}

Prevention

When it happens

Trigger: Writing `interface Foo {}` with an empty body; writing `interface Foo extends Bar {}` that adds no new members; an ambient .d.ts stub whose members were all removed; an interface whose name does not match the allowWithName regex.

Common situations: Placeholder types left over from prototyping, Flow-to-TypeScript migrations where empty interfaces were idiomatic, declaration merging that leaves one empty declaration, enabling the typescript restriction preset in oxlint which turns this rule on.

Related errors


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