Skip to main content

prefer-type-fest-merge

Prefer Merge from type-fest over Except<Destination, keyof Source> & Source intersections.

This rule lives only in the experimental preset and reports without autofixing.

Targeted pattern scopeโ€‹

This rule focuses on direct merge-style intersections that already use Except from type-fest to remove overridden keys before intersecting with the overriding source type.

  • Except<Destination, keyof Source> & Source
  • Source & Except<Destination, keyof Source>

It intentionally skips builtin Omit forms and broader intersections to avoid overlapping with stable rules and to keep the report signal narrow.

What this rule reportsโ€‹

This rule reports intersections when all of the following are true:

  • one intersection member resolves to Except
  • the second type argument to Except is exactly keyof Source
  • the sibling intersection member is exactly Source

The rule is currently report-only. It does not autofix or suggest a replacement yet.

Why this rule existsโ€‹

Merge<Destination, Source> states the override relationship directly.

  • Readers do not need to unpack an Except-plus-intersection recipe.
  • The canonical helper signals that the second type wins on overlapping keys.
  • The upstream utility also simplifies the resulting object type for tooling display.

โŒ Incorrectโ€‹

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

type Base = {
id: string;
name: string;
};

type Overrides = {
name: number;
readonly ok: true;
};

type Combined = Except<Base, keyof Overrides> & Overrides;

โœ… Correctโ€‹

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

type Base = {
id: string;
name: string;
};

type Overrides = {
name: number;
readonly ok: true;
};

type Combined = Merge<Base, Overrides>;

Behavior and migration notesโ€‹

  • This rule only reports the exact Except<Destination, keyof Source> & Source pattern.
  • It ignores Omit<Destination, keyof Source> & Source so it does not double-report with prefer-type-fest-except.
  • It ignores intersections whose sibling type does not match the keyof operand exactly.

Additional examplesโ€‹

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

import type {Except as StrictExcept} from "type-fest";

type Base = {
id: string;
name: string;
};

type Overrides = {
name: number;
readonly ok: true;
};

type Combined = Overrides & StrictExcept<Base, keyof Overrides>;

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

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

type Base = {
id: string;
name: string;
};

type Overrides = {
name: number;
readonly ok: true;
};

type Combined = Merge<Base, Overrides>;

ESLint flat config exampleโ€‹

import typefest from "eslint-plugin-typefest";

export default [typefest.configs.experimental];

When not to use itโ€‹

Disable this rule if your team prefers the explicit Except<..., keyof ...> & ... construction, or if you want to keep the override mechanism visible in source types.

Package documentationโ€‹

TypeFest package documentation:

Source file: source/merge.d.ts

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

type Foo = {
a: string;
b: number;
};

type Bar = {
a: number;
c: boolean;
};

type FooBar = Merge<Foo, Bar>;
//=> {a: number; b: number; c: boolean}

Rule catalog ID: R088

Further readingโ€‹

Adoption resourcesโ€‹