Skip to content

Commit 61139a3

Browse files
committed
fix: make compatible with built-in Error, deprecate ErrorLike
1 parent f598794 commit 61139a3

File tree

6 files changed

+169
-89
lines changed

6 files changed

+169
-89
lines changed

.github/workflows/publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,6 @@ jobs:
4141

4242
- name: Publish to npm
4343
run: |
44-
pnpm publish --no-git-checks --tag ${{ steps.version.outputs.releaseType }}
44+
pnpm publish --no-git-checks --access public --tag ${{ steps.version.outputs.releaseType }}
4545
env:
4646
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

README.md

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -214,25 +214,21 @@ Alternatively, if you are extending the `EError` class, you may want to pass on
214214
the generics for improved type support, or define them to restrict them.
215215

216216
```ts
217-
export interface ErrorLike extends Error {
218-
cause?: Error;
219-
}
220-
221217
class EError<
222218
T extends { [key: string]: any } = { [key: string]: any },
223-
Cause extends ErrorLike = ErrorLike
219+
Cause extends Error = Error
224220
> {
225221
// ...
226222
}
227223
```
228224

229225
```ts
230-
import { EError, ErrorLike } from "exceptional-errors";
226+
import { EError } from "exceptional-errors";
231227

232228
// Pass on the generics
233229
class MyCustomError<
234230
T extends { [key: string]: any },
235-
Cause extends ErrorLike
231+
Cause extends Error
236232
> extends EError<T, Cause> {
237233
// ...
238234
}
@@ -248,10 +244,6 @@ class MyCustomError extends EError<{ code: number }> {
248244
```ts
249245
/**
250246
* Create an EError instance with an empty message.
251-
*
252-
* This is not recommended, but is provided as an option for when
253-
* you extend the class and you don't need to specify a message
254-
* if the class name itself gives enough context.
255247
*/
256248
new EError()
257249

@@ -306,7 +298,7 @@ The following methods are available on any `EError` instance.
306298
```ts
307299
class EError {
308300
// ...
309-
getCauses(filter?: (error: ErrorLike) => boolean): ErrorLike[];
301+
getCauses(filter?: (error: Error) => boolean): Error[];
310302
}
311303
```
312304

@@ -318,8 +310,8 @@ filter function to filter the returned results.
318310
```ts
319311
class EError {
320312
// ...
321-
findCause<T extends Error>(type: ErrorLikeConstructor<T>): T | null;
322-
findCauses<T extends Error>(type: ErrorLikeConstructor<T>): T[];
313+
findCause<T extends Error>(type: AnyErrorConstructor<T>): T | null;
314+
findCauses<T extends Error>(type: AnyErrorConstructor<T>): T[];
323315
}
324316
```
325317

@@ -333,8 +325,8 @@ built-in `Error` class, `EError`, or a custom error class.
333325
```ts
334326
class EError {
335327
// ...
336-
findCauseByName(name: string): ErrorLike | null;
337-
findCausesByName(name: string): ErrorLike[];
328+
findCauseByName(name: string): Error | null;
329+
findCausesByName(name: string): Error[];
338330
}
339331
```
340332

@@ -411,7 +403,7 @@ This package has been tested to be compatible with ES6 and CommonJS. To run the
411403
tests in this module, you'll need to clone this repository and install the
412404
development dependencies.
413405

414-
To run tests for CommonJS, run the following command:
406+
To run tests for CommonJS and Typescript run the following command:
415407

416408
```
417409
pnpm test

package.json

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@
2525
"files": [
2626
"dist/**"
2727
],
28-
"publishConfig": {
29-
"access": "public"
30-
},
3128
"scripts": {
3229
"prepare": "husky install",
3330
"format": "prettier --write 'src/**/*.ts'",
@@ -37,9 +34,10 @@
3734
"build:esm": "tsc --outDir dist/esm --declaration && tsc --outDir dist/esm --module ES2015 --target ES2015 --removeComments",
3835
"test": "pnpm test:build && pnpm test:cjs",
3936
"test:build": "concurrently pnpm:test:build:* -n test:build:",
40-
"test:build:cjs": "tsc --outDir build/cjs --module CommonJS",
41-
"test:build:es2015": "tsc --outDir build/es2015 --module ES2015",
42-
"test:build:es2022": "tsc --outDir build/es2022 --module ES2022",
37+
"test:build:cjs": "tsc --outDir build/cjs --module CommonJS --rootDir src",
38+
"test:build:es2015": "tsc --outDir build/es2015 --module ES2015 --rootDir src",
39+
"test:build:es2022": "tsc --outDir build/es2022 --module ES2022 --rootDir src",
40+
"test:build:ts": "tsc test/typescript.test.ts --noEmit --lib ES2022,DOM",
4341
"test:cjs": "mocha test/cjs.test.js",
4442
"test:browser": "pnpm test:build && concurrently pnpm:test:browser:* -n test:browser:",
4543
"test:browser:serve": "pnpm serve",

src/index.ts

Lines changed: 70 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,13 @@
11
/**
2-
* An {@link Error} object that has an optional `cause` field.
3-
*
4-
* This interface is provided to provide compatibiliy with older
5-
* versions of ECMAScript (ES2021 and below), as the `cause` field
6-
* was only implemented in ES2022.
7-
*
8-
* @see https://github.com/tc39/proposal-error-cause
2+
* Type for a class constructor that extends {@link Error}.
93
*/
10-
export interface ErrorLike extends Error {
11-
cause?: Error;
12-
}
4+
export type AnyErrorConstructor<T extends Error = Error> = {
5+
new (...args: any[]): T;
6+
};
137

148
export type EErrorOptions<
159
T extends { [key: string]: any },
16-
Cause extends ErrorLike
10+
Cause extends Error
1711
> = {
1812
/**
1913
* A property indicating the specific cause of the error.
@@ -28,13 +22,6 @@ export type EErrorOptions<
2822
info?: T;
2923
};
3024

31-
/**
32-
* Type for a class definition that creates an {@link ErrorLike} instance.
33-
*/
34-
export type ErrorLikeConstructor<T extends ErrorLike> = {
35-
new (...args: any[]): T;
36-
};
37-
3825
/**
3926
* Normalised JSON format for errors.
4027
*/
@@ -54,33 +41,29 @@ const EMPTY_STACK_TRACE = "<stack trace empty>";
5441
* existing error as the cause and add structured data to help with debugging.
5542
*
5643
* @template T The shape of any structured data to pass to the error. Defaults to the arbitrary type `{ [key: string]: any }`.
57-
* @template Cause The error type that this error may wrap as a cause. Must extend the {@link ErrorLike} type.
44+
* @template Cause The error type that this error may wrap as a cause. Must extend the {@link Error} type.
5845
*/
5946
export class EError<
6047
T extends { [key: string]: any } = { [key: string]: any },
61-
Cause extends ErrorLike = ErrorLike
48+
Cause extends Error = Error
6249
> extends Error {
63-
/**
64-
* The original message passed to the error constructor.
65-
*/
66-
readonly originalMessage: string;
67-
6850
/**
6951
* A property indicating the specific cause of the error.
7052
*
7153
* @override
7254
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause
7355
*/
74-
readonly cause?: Cause;
56+
override readonly cause?: Cause;
57+
58+
/**
59+
* The original message passed to the error constructor.
60+
*/
61+
readonly originalMessage: string;
7562

7663
readonly info?: T;
7764

7865
/**
7966
* Create an {@link EError} instance with an empty message.
80-
*
81-
* This is not recommended, but is provided as an option for when
82-
* you extend the class and you don't need to specify a message
83-
* if the class name itself gives enough context.
8467
*/
8568
constructor();
8669

@@ -183,14 +166,14 @@ export class EError<
183166
* @param filter A function to filter the causes to return. Return `true` if an error in the chain should be included in the result.
184167
*/
185168
static getCauses(
186-
error: ErrorLike | undefined,
187-
filter?: (error: ErrorLike) => boolean
188-
): ErrorLike[] {
189-
const causes: ErrorLike[] = [];
190-
let currentError: ErrorLike | undefined = error;
191-
while (currentError) {
192-
if (!filter || filter(currentError)) causes.push(currentError);
193-
currentError = currentError.cause;
169+
error: Error | undefined,
170+
filter?: (error: Error) => boolean
171+
): Error[] {
172+
const causes: Error[] = [];
173+
let cause: Error | null | undefined = error;
174+
while (cause) {
175+
if (!filter || filter(cause)) causes.push(cause);
176+
cause = cause.cause instanceof Error ? cause.cause : null;
194177
}
195178
return causes;
196179
}
@@ -200,7 +183,7 @@ export class EError<
200183
*
201184
* @param filter A function to filter the causes to return. Return `true` if an error in the chain should be included in the result.
202185
*/
203-
getCauses(filter?: (error: ErrorLike) => boolean): ErrorLike[] {
186+
getCauses(filter?: (error: Error) => boolean): Error[] {
204187
return EError.getCauses(this, filter);
205188
}
206189

@@ -212,14 +195,13 @@ export class EError<
212195
* @param type The error type. Must be a class definition, such as {@link Error}.
213196
*/
214197
static findCause<T extends Error>(
215-
error: ErrorLike | undefined,
216-
type: ErrorLikeConstructor<T>
198+
error: Error | undefined,
199+
type: AnyErrorConstructor<T>
217200
): T | null {
218-
let errorCause: ErrorLike | undefined = error;
219-
while (errorCause) {
220-
if (errorCause instanceof type && errorCause.name === type.name)
221-
return errorCause;
222-
errorCause = (errorCause as ErrorLike).cause;
201+
let cause: Error | null | undefined = error;
202+
while (cause) {
203+
if (cause instanceof type && cause.name === type.name) return cause as T;
204+
cause = cause.cause instanceof Error ? cause.cause : null;
223205
}
224206
return null;
225207
}
@@ -230,7 +212,7 @@ export class EError<
230212
*
231213
* @param type The error type. Must be a class definition, such as {@link Error}.
232214
*/
233-
findCause<T extends Error>(type: ErrorLikeConstructor<T>): T | null {
215+
findCause<T extends Error>(type: AnyErrorConstructor<T>): T | null {
234216
return EError.findCause(this, type);
235217
}
236218

@@ -242,8 +224,8 @@ export class EError<
242224
* @param type The error type. Must be a class definition, such as {@link Error}.
243225
*/
244226
static findCauses<T extends Error>(
245-
error: ErrorLike,
246-
type: ErrorLikeConstructor<T>
227+
error: Error,
228+
type: AnyErrorConstructor<T>
247229
): T[] {
248230
return EError.getCauses(
249231
error,
@@ -257,7 +239,7 @@ export class EError<
257239
*
258240
* @param type The error type. Must be a class definition, such as {@link Error}.
259241
*/
260-
findCauses<T extends Error>(type: ErrorLikeConstructor<T>): T[] {
242+
findCauses<T extends Error>(type: ErrorConstructor): T[] {
261243
return EError.getCauses(
262244
this,
263245
(e) => e instanceof type && e.name === type.name
@@ -272,12 +254,11 @@ export class EError<
272254
* @param error The error to get the cause chain from.
273255
* @param name The name of the error to find.
274256
*/
275-
static findCauseByName(error: ErrorLike, name: string): ErrorLike | null {
276-
let errorCause: ErrorLike | undefined = error;
277-
while (errorCause) {
278-
if (errorCause instanceof Error && errorCause.name === name)
279-
return errorCause;
280-
errorCause = errorCause.cause;
257+
static findCauseByName(error: Error, name: string): Error | null {
258+
let cause: Error | null = error;
259+
while (cause) {
260+
if (cause instanceof Error && cause.name === name) return cause;
261+
cause = cause.cause instanceof Error ? cause.cause : null;
281262
}
282263
return null;
283264
}
@@ -289,7 +270,7 @@ export class EError<
289270
*
290271
* @param name The name of the error to find.
291272
*/
292-
findCauseByName(name: string): ErrorLike | null {
273+
findCauseByName(name: string): Error | null {
293274
return EError.findCauseByName(this, name);
294275
}
295276

@@ -301,7 +282,7 @@ export class EError<
301282
* @param error The error to get the cause chain from.
302283
* @param name The name of the error to find.
303284
*/
304-
static findCausesByName(error: ErrorLike, name: string): ErrorLike[] {
285+
static findCausesByName(error: Error, name: string): Error[] {
305286
return EError.getCauses(error, (e) => e.name === name);
306287
}
307288

@@ -312,7 +293,7 @@ export class EError<
312293
*
313294
* @param name The name of the error to find.
314295
*/
315-
findCausesByName(name: string): ErrorLike[] {
296+
findCausesByName(name: string): Error[] {
316297
return EError.findCausesByName(this, name);
317298
}
318299

@@ -322,9 +303,9 @@ export class EError<
322303
*
323304
* @param error The error to get the full stack trace for.
324305
*/
325-
static fullStack(error: ErrorLike): string {
306+
static fullStack(error: Error): string {
326307
let stack = error.stack ?? EMPTY_STACK_TRACE;
327-
if (error.cause)
308+
if (error.cause instanceof Error)
328309
stack += "\n\n" + `caused by: ${EError.fullStack(error.cause)}`;
329310
return stack;
330311
}
@@ -344,7 +325,7 @@ export class EError<
344325
* @param options Output options.
345326
*/
346327
static toJSON(
347-
error: ErrorLike,
328+
error: Error,
348329
options: {
349330
/** If `true`, includes the stack trace in the output. */
350331
stack?: boolean;
@@ -363,7 +344,7 @@ export class EError<
363344
json.originalMessage = error.originalMessage;
364345
}
365346

366-
if (!shallow && error.cause) {
347+
if (!shallow && error.cause instanceof Error) {
367348
json.cause = EError.toJSON(error.cause, options);
368349
}
369350

@@ -395,3 +376,29 @@ export class EError<
395376
return EError.toJSON(this, options);
396377
}
397378
}
379+
380+
// --- DEPRECATED ---
381+
382+
/**
383+
* An {@link Error} object that has an optional `cause` field.
384+
*
385+
* This interface is provided to provide compatibiliy with older
386+
* versions of ECMAScript (ES2021 and below), as the `cause` field
387+
* was only implemented in ES2022.
388+
*
389+
* @see https://github.com/tc39/proposal-error-cause
390+
*
391+
* @deprecated Use the built-in {@link Error} interface.
392+
*/
393+
export interface ErrorLike extends Error {
394+
cause?: Error;
395+
}
396+
397+
/**
398+
* Type for a class definition that creates an {@link ErrorLike} instance.
399+
*
400+
* @deprecated Use the built-in {@link ErrorConstructor} interface.
401+
*/
402+
export type ErrorLikeConstructor<T extends ErrorLike> = {
403+
new (...args: any[]): T;
404+
};

0 commit comments

Comments
 (0)