{"record":{"id":"c61c6bd14d29c5ff","repo":"oxc-project/oxc","slug":"an-empty-interface-is-equivalent-to","errorCode":null,"errorMessage":"an empty interface is equivalent to `{}`","messagePattern":"an empty interface is equivalent to `(.+?)`","errorType":"validation","errorClass":"OxcDiagnostic","httpStatus":null,"severity":"warning","filePath":"crates/oxc_linter/src/rules/typescript/no_empty_interface.rs","lineNumber":16,"sourceCode":"use oxc_ast::AstKind;\nuse oxc_diagnostics::OxcDiagnostic;\nuse oxc_macros::declare_oxc_lint;\nuse oxc_span::Span;\nuse schemars::JsonSchema;\nuse serde::Deserialize;\nuse serde_json::Value;\n\nuse crate::{\n    AstNode,\n    context::{ContextHost, LintContext},\n    rule::{DefaultRuleConfig, Rule},\n};\n\nfn no_empty_interface_diagnostic(span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(\"an empty interface is equivalent to `{}`\")\n        .with_help(\n            \"Add members to this interface, or use a type alias if it is intentionally empty.\",\n        )\n        .with_label(span)\n}\n\nfn no_empty_interface_extend_diagnostic(span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(\"an interface declaring no members is equivalent to its supertype\")\n        .with_help(\"Remove this interface and use the extended type directly or add members to this interface.\")\n        .with_label(span)\n}\n\n#[derive(Debug, Default, Clone, JsonSchema, Deserialize)]\n#[serde(rename_all = \"camelCase\", default, deny_unknown_fields)]\npub struct NoEmptyInterface {\n    /// When set to `true`, allows empty interfaces that extend a single interface.\n    #[serde(alias = \"allow_single_extends\")]\n    // for backwards-compatibility, we made a mistake in the naming previously","sourceCodeStart":1,"sourceCodeEnd":34,"githubUrl":"https://github.com/oxc-project/oxc/blob/e1e7af627c8843ab64044ed466b128fcc21a035b/crates/oxc_linter/src/rules/typescript/no_empty_interface.rs#L1-L34","documentation":"typescript/no-empty-interface reports `interface Foo {}` — an interface with no body members. An empty interface is structurally equivalent to `{}`, which in TypeScript means \"any non-nullish value\", so it provides no contract. The help suggests adding members or using a type alias if the emptiness is intentional. Note allowSingleExtends does not exempt this variant; it only applies to interfaces that extend exactly one type.","triggerScenarios":"Any TSInterfaceDeclaration with an empty body and no extends clause; declared as a placeholder, an accidental empty block, or a leftover after members were moved elsewhere.","commonSituations":"Scaffolding files where the interface was created but never filled in; merging declarations where ambient augmentation was expected; teams migrating from `no-empty-object-type` conventions; placeholders committed during WIP.","solutions":["Add the intended members to the interface","If intentionally empty, replace with a type alias: `type Foo = {}` is still weak, so prefer modeling what it actually accepts, or keep it documented","If it exists only to be augmented later, disable the rule for that line or wait to add members before committing"],"exampleFix":"// before\ninterface UserPrefs {}\n\n// after\ninterface UserPrefs {\n  theme: 'light' | 'dark';\n  locale: string;\n}","handlingStrategy":"validation","validationCode":"// .oxlintrc.json\n{\n  \"rules\": { \"typescript/no-empty-interface\": \"warn\" }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Fill in interfaces before committing scaffolding; empty types are flagged because {} accepts almost everything","For intentionally weak types, write `type Foo = Record<string, never>` or model the real constraint instead","Let the rule run in CI so placeholder interfaces do not accumulate across branches"],"tags":["typescript","lint","interface","type-safety","oxlint"],"backgroundTag":"empty-interface","analyzedSha":"e1e7af627c8843ab64044ed466b128fcc21a035b","analyzedAt":"2026-08-20T07:01:07.079Z","contentChangedAt":"2026-08-20T07:01:07.079Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}