Skip to main content

prefer-type-fest-promisable

Require TypeFest Promisable<T> for sync-or-async callback contracts currently expressed as Promise<T> | T unions.

Targeted pattern scopeโ€‹

This rule narrows matching to strict two-member Promise/base unions where Promisable<T> is the canonical replacement.

  • Type unions shaped like Promise<T> | T in architecture-critical runtime layers.

Multi-member unions and Promise-adjacent variants stay out of scope unless they exactly match the authored pair form.

What this rule reportsโ€‹

This rule reports strict Promise<T> | T-shaped unions that should be expressed as Promisable<T>.

  • Type unions shaped like Promise<T> | T in architecture-critical runtime layers.
  • Type references that resolve to imported legacy aliases such as MaybePromise.

Detection boundariesโ€‹

  • โœ… Reports strict Promise<T> | T / T | Promise<T> unions by default.
  • โœ… Reports imported legacy aliases such as MaybePromise by default.
  • โŒ Does not report namespace-qualified aliases.
  • โœ… Auto-fixes imported legacy alias references when replacement is syntactically safe.
  • โŒ Does not auto-fix Promise<T> | T union declarations.
  • โœ… Enforcement surface is configurable with enforcePromiseUnions and enforceLegacyAliases.

Why this rule existsโ€‹

Promisable<T> communicates intent directly, keeps callback contracts consistent, and avoids repeating equivalent sync-or-async unions throughout the codebase.

โŒ Incorrectโ€‹

type HookResult = Promise<Result> | Result;

โœ… Correctโ€‹

type HookResult = Promisable<Result>;

Behavior and migration notesโ€‹

  • Promisable<T> captures sync-or-async return contracts in one reusable alias.
  • It normalizes both Promise<T> | T and T | Promise<T> forms.
  • Use this alias in hook/callback contracts where callers may return either immediate or async values.

Optionsโ€‹

This rule accepts a single options object:

type PreferTypeFestPromisableOptions = {
/**
* Whether to report imported legacy aliases such as MaybePromise.
*
* @default true
*/
enforceLegacyAliases?: boolean;

/**
* Whether to report Promise<T> | T sync-or-async unions.
*
* @default true
*/
enforcePromiseUnions?: boolean;
};

Default configuration:

{
enforceLegacyAliases: true,
enforcePromiseUnions: true,
}

Flat config setup (default behavior):

import typefest from "eslint-plugin-typefest";

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

enforcePromiseUnions: falseโ€‹

Ignores union-shaped contracts while still enforcing legacy aliases:

import typefest from "eslint-plugin-typefest";

export default [
{
plugins: { typefest },
rules: {
"typefest/prefer-type-fest-promisable": [
"error",
{
enforceLegacyAliases: true,
enforcePromiseUnions: false,
},
],
},
},
];
import type { MaybePromise } from "type-aliases";

type A = Promise<Result> | Result; // โœ… Not reported
type B = MaybePromise<Result>; // โŒ Reported

enforceLegacyAliases: falseโ€‹

Ignores legacy aliases while still enforcing union-shaped contracts:

import typefest from "eslint-plugin-typefest";

export default [
{
plugins: { typefest },
rules: {
"typefest/prefer-type-fest-promisable": [
"error",
{
enforceLegacyAliases: false,
enforcePromiseUnions: true,
},
],
},
},
];
import type { MaybePromise } from "type-aliases";

type A = MaybePromise<Result>; // โœ… Not reported
type B = Promise<Result> | Result; // โŒ Reported

Additional examplesโ€‹

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

type MaybeAsync = Result | Promise<Result>;

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

type MaybeAsync = Promisable<Result>;

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

type HookOutput = Promisable<void>;

ESLint flat config exampleโ€‹

import typefest from "eslint-plugin-typefest";

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

When not to use itโ€‹

Disable this rule if runtime policy requires explicitly spelling out promise unions.

Package documentationโ€‹

TypeFest package documentation:

Source file: source/promisable.d.ts

/**
Create a type that represents either the value or the value wrapped in `PromiseLike`.

Use-cases:
- A function accepts a callback that may either return a value synchronously or may return a promised value.
- This type could be the return type of `Promise#then()`, `Promise#catch()`, and `Promise#finally()` callbacks.

Please upvote [this issue](https://github.com/microsoft/TypeScript/issues/31394) if you want to have this type as a built-in in TypeScript.

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

async function logger(getLogEntry: () => Promisable<string>): Promise<void> {
const entry = await getLogEntry();
console.log(entry);
}

await logger(() => 'foo');
await logger(() => Promise.resolve('bar'));
```

@category Async
*/

Rule catalog ID: R054

Further readingโ€‹

Adoption resourcesโ€‹