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)).
*
* @category Object
*
* @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'}
* ```
*/

Rule catalog ID: R040

Further readingโ€‹

Adoption resourcesโ€‹