Skip to main content

consistent-type-definitions

Enforce type definitions to consistently use either interface or type.

🎨

Extending "plugin:@typescript-eslint/stylistic" in an ESLint configuration enables this rule.

🔧

Some problems reported by this rule are automatically fixable by the --fix ESLint command line option.

TypeScript provides two common ways to define an object type: interface and type.

// type alias
type T1 = {
a: string;
b: number;
};

// interface keyword
interface T2 {
a: string;
b: number;
}

The two are generally very similar, and can often be used interchangeably. Using the same type declaration style consistently helps with code readability.

eslint.config.mjs
export default tseslint.config({
rules: {
"@typescript-eslint/consistent-type-definitions": "error"
}
});

Try this rule in the playground ↗

Options

This rule accepts the following options:

type Options = [
/** Which type definition syntax to prefer. */
| 'type'
/** Which type definition syntax to prefer. */
| 'interface',
];

const defaultOptions: Options = ['interface'];
  • 'interface' (default): enforce using interfaces for object type definitions.
  • 'type': enforce using types for object type definitions.

'interface'

type T = { x: number };
Open in Playground

'type'

interface T {
x: number;
}
Open in Playground

FAQs

What are the differences between interface and type?

There are very few differences between interfaces and object types in TypeScript. Other than type aliases being used to represent union types, it is rare that you will need to choose one over the other.

FeatureInterfacesObject TypesExplanation
Object shapesBoth can be used to represent general object shapes.
General performanceBoth are optimized for performance in TypeScript's type checker.
Edge case performanceLarge, complex logical types can be optimized better with interfaces by TypeScript's type checker.
Traditional semanticsInterfaces are typically the default in much -though not all- of the TypeScript community.
Non-object shapesObject types may describe literals, primitives, unions, and intersections.
Logical typesObject types may include conditional and mapped types.
MergingAllowedNot allowedInterfaces of the same name are treated as one interface ("merged"); type aliases may not share names.

We recommend choosing one definition style, using it when possible, and falling back to the other style when needed. The benefits of remaining consistent within a codebase almost always outweigh the benefits of either definition style.

When do the performance differences between interface and type matter?

Almost never. Most TypeScript projects do not -and should not- utilize types that exercise the performance differences between the two kinds of definitions.

If you are having problems with type checking performance, see the TypeScript Wiki's Performance page.

Why is the default interface?

Interfaces are the prevailing, most common style in the TypeScript. interface has traditionally been TypeScript's intended ("semantic") way to convey "an object with these fields".

We generally recommend staying with the default, 'interface', to be stylistically consistent with the majority of TypeScript projects. If you strongly prefer 'type', that's fine too.

When Not To Use It

If you specifically want to manually choose whether to use an interface or type literal for stylistic reasons each time you define a type, you can avoid this rule.

However, keep in mind that inconsistent style can harm readability in a project. We recommend picking a single option for this rule that works best for your project.

You might occasionally need to a different definition type in specific cases, such as if your project is a dependency or dependent of another project that relies on a specific type definition style. Consider using ESLint disable comments for those specific situations instead of completely disabling this rule.

Further Reading

Resources