TypeScript Retry Decorator
The `typescript-retry-decorator` library provides a simple, zero-dependency decorator for adding retry logic to TypeScript functions, inspired by Spring-Retry. It supports both synchronous and asynchronous (Promise-returning) functions, making it suitable for Node.js and modern browser environments. The current stable version is 2.5.4, with releases occurring periodically, often addressing compatibility with newer TypeScript features or Node.js module systems, as seen in recent 2.5.x patches. Its key differentiators include its lightweight nature (no external dependencies), comprehensive test coverage, and support for both legacy and TypeScript 5+ standard decorators, offering various backoff policies (fixed, exponential with jitter) and custom error handling for retry conditions.
Common errors
-
TypeError: (0 , _typescriptRetryDecorator.Retry) is not a function
cause Attempting to import and use the library in a CommonJS environment that doesn't correctly resolve the dual CJS/ESM package exports, especially after recent ESM compatibility fixes (v2.5.x). This often happens when `require()` is used on a package primarily structured for ESM.fixIf using Node.js and an specific CommonJS setup, try updating Node.js, your bundler, or TypeScript configuration. For most modern setups, switch to `import { Retry } from 'typescript-retry-decorator';`. Ensure `package.json` for your project either has `"type": "module"` (for ESM) or correctly handles CJS interop if `"type": "commonjs"`. -
Decorator 'Retry' is not a valid decorator factory.
cause Incorrect `tsconfig.json` configuration for decorators, particularly missing `"experimentalDecorators": true` or `"emitDecoratorMetadata": true` when using legacy decorators, or issues with TS5+ standard decorator setup.fixEnsure `"experimentalDecorators": true` and `"emitDecoratorMetadata": true` are present in your `tsconfig.json` if using TypeScript versions prior to 5.0 or opting into legacy decorators. If using TS 5.0+, ensure no conflicting experimental decorator flags are set and your TypeScript version is correctly configured. -
TS2345: Argument of type 'string' is not assignable to parameter of type 'ErrorConstructor | undefined'.
cause Passing a string or simple object to the `value` option instead of an array of actual `Error` constructor functions (e.g., `Error`, `MyCustomError`). The `value` option expects an array of error *classes* to retry on.fixEnsure the `value` option is an array containing constructor functions of `Error` types, like `value: [Error, CustomTransientError]`.
Warnings
- breaking Post v2.5.2, significant changes were made to how the package handles CommonJS (CJS) and ES Modules (ESM) builds. Versions 2.5.3 and 2.5.4 specifically addressed issues with `.js` extensions in imports and the `type: module` declaration, which might break existing CJS `require()` usage or older ESM bundler setups.
- gotcha The library supports both TypeScript's legacy experimental decorators (`experimentalDecorators` compiler option) and the new TypeScript 5+ standard decorators. Incorrect `tsconfig.json` setup can lead to decorator compilation failures or runtime issues.
- gotcha By default, when `maxAttempts` are exhausted, the decorator throws a `MaxAttemptsError`. To rethrow the original exception that caused the last failure, the `useOriginalError` option must be set to `true`.
- gotcha Version 2.4.2 enabled `strictNullChecks` for the package itself, potentially exposing previously overlooked null/undefined handling issues in consuming applications that relied on more lenient type checking.
Install
-
npm install typescript-retry-decorator -
yarn add typescript-retry-decorator -
pnpm add typescript-retry-decorator
Imports
- Retry
const { Retry } = require('typescript-retry-decorator');import { Retry } from 'typescript-retry-decorator'; - FixedBackOffPolicy, ExponentialBackOffPolicy
import { FixedBackOffPolicy } from 'typescript-retry-decorator/dist/cjs';import { FixedBackOffPolicy, ExponentialBackOffPolicy } from 'typescript-retry-decorator'; - RetryOptions
import type { RetryOptions } from 'typescript-retry-decorator';
Quickstart
import { Retry, ExponentialBackOffPolicy } from 'typescript-retry-decorator';
// Simulate an external service call that fails a few times
let callCount = 0;
async function unreliableServiceCall(data: string): Promise<string> {
callCount++;
if (callCount < 3) { // Fail for the first 2 calls
console.log(`Attempt ${callCount}: Service call failed for data: ${data}`);
throw new Error('Service temporarily unavailable');
}
console.log(`Attempt ${callCount}: Service call succeeded for data: ${data}`);
return `Processed: ${data}`;
}
class MyService {
@Retry({
maxAttempts: 5,
backOff: 100, // initial backoff 100ms
backOffPolicy: ExponentialBackOffPolicy,
exponentialOption: { multiplier: 2, maxInterval: 1000 }, // Intervals: 100, 200, 400, 800, 1000, 1000...
doRetry: (e: any) => {
console.log(`Checking error for retry: ${e.message}`);
return e.message === 'Service temporarily unavailable';
},
value: [Error], // Retry on any Error class
useOriginalError: true, // Throw original error if max attempts reached
useConsoleLogger: true
})
async processData(input: string): Promise<string> {
console.log(`Executing processData for input: ${input}`);
return unreliableServiceCall(input);
}
}
async function runExample() {
const service = new MyService();
try {
const result = await service.processData('example-data');
console.log(`Final Result: ${result}`);
} catch (error: any) {
console.error(`Operation failed after retries: ${error.message}`);
} finally {
callCount = 0; // Reset for potential re-runs
}
}
runExample();