Skip to main content

prefer-type-fest-union-to-intersection

Prefer UnionToIntersection from type-fest over custom distributive conditional helpers that convert unions into intersections.

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

Targeted pattern scopeโ€‹

This rule focuses on the canonical conditional-type recipe that turns a union into an intersection by distributing a function wrapper over the union and then inferring the merged parameter type.

  • (Union extends unknown ? (value: Union) => void : never) extends (value: infer Intersection) => void ? Intersection : never
  • the common assignable variant that returns Intersection & Union

It intentionally skips unrelated conditional helpers and non-canonical extraction tricks to keep reporting narrow.

What this rule reportsโ€‹

This rule reports conditional helpers when all of the following are true:

  • the inner conditional distributes over the same Union type with extends unknown or extends any
  • the inner true branch wraps that union in a single-parameter function type
  • the outer conditional infers the merged parameter type from a single-parameter function type
  • the outer true branch returns either the inferred type alone or the inferred type intersected with the original union

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

Why this rule existsโ€‹

UnionToIntersection<Union> states the intent directly.

  • Readers do not need to unpack a dense conditional-type trick.
  • The canonical helper is easier to search for across a codebase.
  • Type-Fest owns the edge cases and maintenance burden for the pattern.

โŒ Incorrectโ€‹

type MergeUnion<Union> =
(Union extends unknown ? (value: Union) => void : never) extends
(value: infer Intersection) => void
? Intersection
: never;

โœ… Correctโ€‹

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

type MergeUnion<Union> = UnionToIntersection<Union>;

Behavior and migration notesโ€‹

  • This rule only reports the narrow distributive function-wrapper pattern.
  • It also accepts the Intersection & Union variant because that still expresses the same underlying conversion.
  • It ignores unrelated conditionals and other custom extraction helpers that do not match the canonical structure closely enough.

Additional examplesโ€‹

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

type MergeUnion<Union> =
(Union extends any ? (value: Union) => void : never) extends
(value: infer Intersection) => void
? Intersection & Union
: never;

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

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

type MergeUnion<Union> = UnionToIntersection<Union>;

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 project intentionally prefers hand-written conditional-type recipes for educational reasons or if you are using a different local utility type and do not want to standardize on Type-Fest.

Package documentationโ€‹

TypeFest package documentation:

Source file: source/union-to-intersection.d.ts

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

type Union = {a: string} | {b: number};

type Combined = UnionToIntersection<Union>;
//=> {a: string} & {b: number}

Rule catalog ID: R089

Further readingโ€‹

Adoption resourcesโ€‹