Why is this an issue?

In TypeScript, intersections are used to combine multiple types into a single one. An intersection type is represented using the ampersand symbol &. It allows you to combine multiple types into a single type that includes all the properties and methods from each type, thus creating more flexible and powerful type definitions.

However, some of the basic types of TypeScript should not be used with intersections:

Additionally, an intersection with a type without members (for example, {}) doesn’t change the resulting type, is redundant, and can be safely removed from the intersection.

type Foo = T & null; // Noncompliant: 'Foo' is always 'never'

type Bar = T & any; // Noncompliant: 'Bar' is always 'any'

type Baz = T & U & {}; // Noncompliant: '{}' has no members and is redundant

Use consistent types that accurately reflect the domain of values of the defined data type.

type Foo = T | null;

type Bar = T & U;

type Baz = T & U;

Exceptions

No issue will be raised when & {} is used with generic type patterns where the empty type serves a legitimate purpose for type normalization or non-nullability constraints.

The following patterns are recognized:

These patterns are common TypeScript idioms for advanced type manipulation.

// Compliant by exception: Simplify/Prettify pattern forces type flattening
type Simplify<T> = { [K in keyof T]: T[K] } & {};

// Compliant by exception: Generic type normalization
interface SomeProps<T> { value: T; }
type ExtendedProps<T> = SomeProps<T> & {};

// Compliant by exception: Non-nullability constraint
type NonNullish<T> = T & {};

// Compliant by exception: Reversed order also works
type DeepPartialSet<T> = {} & Set<DeepPartial<T>>;

Resources

Documentation