Skip to main content

prefer-type-fest-except

Require TypeFest Except<T, K> over Omit<T, K> when removing keys from object types.

Targeted pattern scopeโ€‹

This rule targets Omit<T, K> object-shaping references and imported aliases such as HomomorphicOmit that can be replaced by the TypeFest canonical utility.

What this rule reportsโ€‹

  • Type references shaped like Omit<T, K>.
  • Type references that resolve to imported HomomorphicOmit aliases.

Detection boundariesโ€‹

  • โœ… Reports built-in Omit<T, K> by default.
  • โœ… Reports imported aliases with direct named imports.
  • โŒ Does not report namespace-qualified aliases.
  • โœ… Auto-fixes imported alias references to Except when replacement is syntactically safe.
  • โŒ Does not auto-fix built-in Omit<T, K> references.
  • โœ… Built-in Omit<T, K> coverage is configurable with enforceBuiltinOmit.

Why this rule existsโ€‹

Except<T, K> from type-fest models omitted keys more strictly and keeps object-shaping conventions aligned with other TypeFest utilities used in this plugin.

โŒ Incorrectโ€‹

type PublicUser = Omit<User, "password">;

โœ… Correctโ€‹

type PublicUser = Except<User, "password">;

Behavior and migration notesโ€‹

  • Except<T, K> is the canonical object-key removal utility in this plugin's type-fest style.
  • Migrate direct Omit<T, K> aliases in shared contracts to keep one naming convention.
  • Review constraint behavior if existing helper wrappers add semantics beyond key omission.

Optionsโ€‹

This rule accepts a single options object:

type PreferTypeFestExceptOptions = {
/**
* Whether to report built-in Omit<T, K> usages.
*
* @default true
*/
enforceBuiltinOmit?: boolean;
};

Default configuration:

{
enforceBuiltinOmit: true,
}

Flat config setup (default behavior):

import typefest from "eslint-plugin-typefest";

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

enforceBuiltinOmit: true (default)โ€‹

Reports both built-in Omit<T, K> and imported aliases.

enforceBuiltinOmit: falseโ€‹

Reports imported aliases, but ignores built-in Omit<T, K>:

import typefest from "eslint-plugin-typefest";

export default [
{
plugins: { typefest },
rules: {
"typefest/prefer-type-fest-except": [
"error",
{ enforceBuiltinOmit: false },
],
},
},
];
import type { HomomorphicOmit } from "type-aliases";

type A = Omit<User, "password">; // โœ… Not reported
type B = HomomorphicOmit<User, "password">; // โŒ Reported

Additional examplesโ€‹

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

type PublicUser = Omit<User, "password" | "token">;

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

type PublicUser = Except<User, "password" | "token">;

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

type Internalless = Except<ApiResponse, "internal">;

ESLint flat config exampleโ€‹

import typefest from "eslint-plugin-typefest";

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

When not to use itโ€‹

Disable this rule if public type contracts intentionally expose Omit as part of the API surface.

Package documentationโ€‹

TypeFest package documentation:

Source file: source/except.d.ts

/**
Create a type from an object type without certain keys.

We recommend setting the `requireExactProps` option to `true`.

This type is a stricter version of [`Omit`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-5.html#the-omit-helper-type). The `Omit` type does not restrict the omitted keys to be keys present on the given type, while `Except` does. The benefits of a stricter type are avoiding typos and allowing the compiler to pick up on rename refactors automatically.

This type was proposed to the TypeScript team, which declined it, saying they prefer that libraries implement stricter versions of the built-in types ([microsoft/TypeScript#30825](https://github.com/microsoft/TypeScript/issues/30825#issuecomment-523668235)).

@example
```
import type {Except} from 'type-fest';

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

type FooWithoutA = Except<Foo, 'a'>;
//=> {b: string}

// @ts-expect-error
const fooWithoutA: FooWithoutA = {a: 1, b: '2'};
// errors: 'a' does not exist in type '{ b: string; }'

type FooWithoutB = Except<Foo, 'b', {requireExactProps: true}>;
//=> {a: number} & Partial<Record<'b', never>>

// @ts-expect-error
const fooWithoutB: FooWithoutB = {a: 1, b: '2'};
// errors at 'b': Type 'string' is not assignable to type 'undefined'.

// The `Omit` utility type doesn't work when omitting specific keys from objects containing index signatures.

// Consider the following example:

type UserData = {
[metadata: string]: string;
email: string;
name: string;
role: 'admin' | 'user';
};

// `Omit` clearly doesn't behave as expected in this case:
type PostPayload = Omit<UserData, 'email'>;
//=> {[x: string]: string; [x: number]: string}

// In situations like this, `Except` works better.
// It simply removes the `email` key while preserving all the other keys.
type PostPayloadFixed = Except<UserData, 'email'>;
//=> {[x: string]: string; name: string; role: 'admin' | 'user'}
```

@category Object
*/

Rule catalog ID: R040

Further readingโ€‹

Adoption resourcesโ€‹