no-empty-object-type
Disallow accidentally using the "empty object" type.
Extending "plugin:@typescript-eslint/recommended"
in an ESLint configuration enables this rule.
Some problems reported by this rule are manually fixable by editor suggestions.
The {}
, or "empty object" type in TypeScript is a common source of confusion for developers unfamiliar with TypeScript's structural typing.
{}
represents any non-nullish value, including literals like 0
and ""
:
let anyNonNullishValue: {} = 'Intentionally allowed by TypeScript.';
Often, developers writing {}
actually mean either:
object
: representing any object valueunknown
: representing any value at all, includingnull
andundefined
In other words, the "empty object" type {}
really means "any value that is defined".
That includes arrays, class instances, functions, and primitives such as string
and symbol
.
To avoid confusion around the {}
type allowing any non-nullish value, this rule bans usage of the {}
type.
That includes interfaces and object type aliases with no fields.
If you do have a use case for an API allowing {}
, you can always configure the rule's options, use an ESLint disable comment, or disable the rule in your ESLint config.
Note that this rule does not report on:
{}
as a type constituent in an intersection type (e.g. types like TypeScript's built-intype NonNullable<T> = T & {}
), as this can be useful in type system operations.- Interfaces that extend from multiple other interfaces.
module.exports = {
"rules": {
"@typescript-eslint/no-empty-object-type": "error"
}
};
Try this rule in the playground ↗
Examples
- ❌ Incorrect
- ✅ Correct
let anyObject: {};
let anyValue: {};
interface AnyObjectA {}
interface AnyValueA {}
type AnyObjectB = {};
type AnyValueB = {};
Open in Playgroundlet anyObject: object;
let anyValue: unknown;
type AnyObjectA = object;
type AnyValueA = unknown;
type AnyObjectB = object;
type AnyValueB = unknown;
let objectWith: { property: boolean };
interface InterfaceWith {
property: boolean;
}
type TypeWith = { property: boolean };
Open in PlaygroundOptions
This rule accepts the following options:
type Options = [
{
allowInterfaces?: 'always' | 'never' | 'with-single-extends';
allowObjectTypes?: 'always' | 'never';
allowWithName?: string;
},
];
const defaultOptions: Options = [
{ allowInterfaces: 'never', allowObjectTypes: 'never' },
];
By default, this rule flags both interfaces and object types.
allowInterfaces
Whether to allow empty interfaces, as one of:
'always'
: to always allow interfaces with no fields'never'
(default): to never allow interfaces with no fields'with-single-extends'
: to allow empty interfaces thatextend
from a single base interface
Examples of correct code for this rule with { allowInterfaces: 'with-single-extends' }
:
interface Base {
value: boolean;
}
interface Derived extends Base {}
Open in PlaygroundallowObjectTypes
Whether to allow empty object type literals, as one of:
'always'
: to always allow object type literals with no fields'never'
(default): to never allow object type literals with no fields
allowWithName
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
.
Examples of code for this rule with { allowWithName: 'Props$' }
:
- ❌ Incorrect
- ✅ Correct
interface InterfaceValue {}
type TypeValue = {};
Open in Playgroundinterface InterfaceProps {}
type TypeProps = {};
Open in PlaygroundWhen Not To Use It
If your code commonly needs to represent the "any non-nullish value" type, this rule may not be for you. Projects that extensively use type operations such as conditional types and mapped types oftentimes benefit from disabling this rule.
Further Reading
- Enhancement: [ban-types] Split the ban into a separate, better-phrased rule
- The Empty Object Type in TypeScript