-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathextend-error.ts
88 lines (74 loc) · 3.01 KB
/
extend-error.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import { addInspectMethod } from "./isomorphic.node";
import { isLazyStack, isWritableStack, joinStacks, lazyJoinStacks } from "./stack";
import { getDeepKeys, toJSON } from "./to-json";
import { ErrorLike, ErrorPOJO, OnoError } from "./types";
const protectedProps: Array<string | symbol> = ["name", "message", "stack"];
/**
* Extends the new error with the properties of the original error and the `props` object.
*
* @param newError - The error object to extend
* @param originalError - The original error object, if any
* @param props - Additional properties to add, if any
*/
export function extendError<TError extends ErrorLike, TOriginal extends ErrorLike, TProps extends object>(
error: TError, originalError?: TOriginal, props?: TProps) {
let onoError = error as unknown as TError & TOriginal & TProps & OnoError<TError & TOriginal & TProps>;
extendStack(onoError, originalError);
// Copy properties from the original error
if (originalError && typeof originalError === "object") {
mergeErrors(onoError, originalError);
}
// The default `toJSON` method doesn't output props like `name`, `message`, `stack`, etc.
// So replace it with one that outputs every property of the error.
onoError.toJSON = toJSON;
// On Node.js, add support for the `util.inspect()` method
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (addInspectMethod) {
addInspectMethod(onoError);
}
// Finally, copy custom properties that were specified by the user.
// These props OVERWRITE any previous props
if (props && typeof props === "object") {
Object.assign(onoError, props);
}
return onoError;
}
/**
* Extend the error stack to include its cause
*/
function extendStack(newError: ErrorLike, originalError?: ErrorPOJO): void {
let stackProp = Object.getOwnPropertyDescriptor(newError, "stack");
if (isLazyStack(stackProp)) {
lazyJoinStacks(stackProp, newError, originalError);
}
else if (isWritableStack(stackProp)) {
newError.stack = joinStacks(newError, originalError);
}
}
/**
* Merges properties of the original error with the new error.
*
* @param newError - The error object to extend
* @param originalError - The original error object, if any
*/
function mergeErrors(newError: ErrorLike, originalError: ErrorPOJO) {
// Get the original error's keys
// NOTE: We specifically exclude properties that we have already set on the new error.
// This is _especially_ important for the `stack` property, because this property has
// a lazy getter in some environments
let keys = getDeepKeys(originalError, protectedProps);
// HACK: We have to cast the errors to `any` so we can use symbol indexers.
// see https://github.com/Microsoft/TypeScript/issues/1863
let _newError = newError as any;
let _originalError = originalError as any;
for (let key of keys) {
if (_newError[key] === undefined) {
try {
_newError[key] = _originalError[key];
}
catch (e) {
// This property is read-only, so it can't be copied
}
}
}
}