Skip to main content

prefer-type-fest-simplify

Require TypeFest Simplify<T> over imported Prettify<T> / Expand<T> aliases.

Targeted pattern scopeโ€‹

This rule only matches type references that resolve to imported Prettify or Expand aliases and can be rewritten as Simplify<T>.

  • Type references that resolve to imported Prettify aliases.
  • Type references that resolve to imported Expand aliases.

Syntactically similar alternatives are intentionally out of scope unless they preserve the same AST shape.

What this rule reportsโ€‹

This rule reports imported Prettify / Expand alias usages that should be replaced with Simplify<T>.

  • Type references that resolve to imported Prettify aliases.
  • Type references that resolve to imported Expand aliases.

Why this rule existsโ€‹

Simplify is the canonical flattening utility provided by type-fest. Standardizing on it reduces utility-name churn across codebases and keeps helper usage consistent with TypeFest defaults.

โŒ Incorrectโ€‹

import type { Prettify } from "type-aliases";

type ViewModel = Prettify<Base & Extra>;

โœ… Correctโ€‹

import type { Simplify } from "type-fest";

type ViewModel = Simplify<Base & Extra>;

Behavior and migration notesโ€‹

  • Simplify<T> normalizes intersections and mapped-type output into a flattened object shape for editor display and assignability workflows.
  • This rule targets imported alias names with overlapping semantics (Prettify, Expand).
  • Keep aliases only when they intentionally add semantics beyond plain type flattening.

Additional examplesโ€‹

โŒ Incorrect โ€” Additional exampleโ€‹

import type { Expand } from "type-aliases";

type UIModel = Expand<Base & Extra>;

โœ… Correct โ€” Additional exampleโ€‹

import type { Simplify } from "type-fest";

type UIModel = Simplify<Base & Extra>;

โœ… Correct โ€” Repository-wide usageโ€‹

type ApiModel = Simplify<ResponseBase & ResponseExtra>;

ESLint flat config exampleโ€‹

import typefest from "eslint-plugin-typefest";

export default [
{
plugins: { typefest },
rules: {
"typefest/prefer-type-fest-simplify": "error",
},
},
];

When not to use itโ€‹

Disable this rule if internal tooling depends on existing alias names.

Package documentationโ€‹

TypeFest package documentation:

Source file: source/simplify.d.ts

/**
* Useful to flatten the type output to improve type hints shown in editors. And
* also to transform an interface into a type to aide with assignability.
*
* @category Object
*
* @example
* ```
* import type {Simplify} from 'type-fest';
*
* type PositionProps = {
* top: number;
* left: number;
* };
*
* type SizeProps = {
* width: number;
* height: number;
* };
*
* // In your editor, hovering over `Props` will show a flattened object with all the properties.
* type Props = Simplify<PositionProps & SizeProps>;
* ```
*
* Sometimes it is desired to pass a value as a function argument that has a different type. At first inspection it may seem assignable, and then you discover it is not because the `value`'s type definition was defined as an interface. In the following example, `fn` requires an argument of type `Record<string, unknown>`. If the value is defined as a literal, then it is assignable. And if the `value` is defined as type using the `Simplify` utility the value is assignable. But if the `value` is defined as an interface, it is not assignable because the interface is not sealed and elsewhere a non-string property could be added to the interface.
*
* If the type definition must be an interface (perhaps it was defined in a third-party npm package), then the `value` can be defined as `const value: Simplify<SomeInterface> = ...`. Then `value` will be assignable to the `fn` argument. Or the `value` can be cast as `Simplify<SomeInterface>` if you can't re-declare the `value`.
*
* @example
* ```
* import type {Simplify} from 'type-fest';
*
* interface SomeInterface {
* foo: number;
* bar?: string;
* baz: number | undefined;
* }
*
* type SomeType = {
* foo: number;
* bar?: string;
* baz: number | undefined;
* };
*
* const literal = {foo: 123, bar: 'hello', baz: 456};
* const someType: SomeType = literal;
* const someInterface: SomeInterface = literal;
*
* declare function fn(object: Record<string, unknown>): void;
*
* fn(literal); // Good: literal object type is sealed
* fn(someType); // Good: type is sealed
* // @ts-expect-error
* fn(someInterface); // Error: Index signature for type 'string' is missing in type 'someInterface'. Because `interface` can be re-opened
* fn(someInterface as Simplify<SomeInterface>); // Good: transform an `interface` into a `type`
* ```
*
* @link https://github.com/microsoft/TypeScript/issues/15300
*
* @see {@link SimplifyDeep}
*/

Rule catalog ID: R066

Further readingโ€‹

Adoption resourcesโ€‹