Skip to content

Commit d495f67

Browse files
committed
refactor: migrate all throw to raise() for global error handling
- Replace 50 throw new TypeError/Error/RangeError with raise() - Add fallback returns for non-throw modes (return 0, continue, return false) - 15 source files updated with raise import - Add error-handling.test.ts: 18 tests covering throw/warn/error/silent modes - Total: 2614 tests, 83 suites, 0 errors
1 parent abcc1d4 commit d495f67

17 files changed

Lines changed: 219 additions & 68 deletions

File tree

src/data-structures/base/iterable-element-base.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { ElementCallback, IterableElementBaseOptions, ReduceElementCallback } from '../../types';
2+
import { raise } from '../../common';
23

34
/**
45
* Base class that makes a data structure iterable and provides common
@@ -25,7 +26,7 @@ export abstract class IterableElementBase<E, R> implements Iterable<E> {
2526
if (options) {
2627
const { toElementFn } = options;
2728
if (typeof toElementFn === 'function') this._toElementFn = toElementFn;
28-
else if (toElementFn) throw new TypeError('toElementFn must be a function type');
29+
else if (toElementFn) raise(TypeError, 'toElementFn must be a function type');
2930
}
3031
}
3132

@@ -224,7 +225,7 @@ export abstract class IterableElementBase<E, R> implements Iterable<E> {
224225
acc = initialValue as U;
225226
} else {
226227
const first = iter.next();
227-
if (first.done) throw new TypeError('Reduce of empty structure with no initial value');
228+
if (first.done) raise(TypeError, 'Reduce of empty structure with no initial value');
228229
acc = first.value as unknown as U;
229230
index = 1;
230231
}

src/data-structures/binary-tree/binary-indexed-tree.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com>
66
* @license MIT License
77
*/
8-
import { ERR } from '../../common';
8+
import { ERR, raise } from '../../common';
99

1010
/**
1111
* Binary Indexed Tree (Fenwick Tree).
@@ -44,7 +44,7 @@ export class BinaryIndexedTree implements Iterable<number> {
4444
}
4545
} else {
4646
if (!Number.isInteger(sizeOrElements) || sizeOrElements < 0) {
47-
throw new RangeError(ERR.invalidArgument('size must be a non-negative integer', 'BinaryIndexedTree'));
47+
raise(RangeError, ERR.invalidArgument('size must be a non-negative integer', 'BinaryIndexedTree'));
4848
}
4949
this._size = sizeOrElements;
5050
this._tree = new Array(this._size + 1).fill(0);
@@ -707,10 +707,10 @@ export class BinaryIndexedTree implements Iterable<number> {
707707

708708
protected _checkIndex(index: number): void {
709709
if (!Number.isInteger(index)) {
710-
throw new TypeError(ERR.invalidIndex('BinaryIndexedTree'));
710+
raise(TypeError, ERR.invalidIndex('BinaryIndexedTree'));
711711
}
712712
if (index < 0 || index >= this._size) {
713-
throw new RangeError(ERR.indexOutOfRange(index, 0, this._size - 1, 'BinaryIndexedTree'));
713+
raise(RangeError, ERR.indexOutOfRange(index, 0, this._size - 1, 'BinaryIndexedTree'));
714714
}
715715
}
716716

src/data-structures/binary-tree/binary-tree.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import { IBinaryTree } from '../../interfaces';
2828
import { isComparable, makeTrampoline, makeTrampolineThunk } from '../../utils';
2929
import { Queue } from '../queue';
3030
import { IterableEntryBase } from '../base';
31-
import { DFSOperation, ERR, Range } from '../../common';
31+
import { DFSOperation, ERR, raise, Range } from '../../common';
3232

3333
/**
3434
* @template K - The type of the key.
@@ -216,7 +216,7 @@ export class BinaryTreeNode<K = any, V = any> {
216216
* node?: BinaryTreeNode<string> | null,
217217
* conditions?: { [key: string]: boolean }
218218
* ): string {
219-
* if (!node) throw new Error('Invalid node');
219+
* if (!node) raise(Error, 'Invalid node');
220220
*
221221
* // If it's a leaf node, return the decision result
222222
* if (!node.left && !node.right) return node.key;
@@ -261,7 +261,7 @@ export class BinaryTreeNode<K = any, V = any> {
261261
* case '/':
262262
* return rightValue !== 0 ? leftValue / rightValue : 0; // Handle division by zero
263263
* default:
264-
* throw new Error(`Unsupported operator: ${node.key}`);
264+
* raise(Error, `Unsupported operator: ${node.key}`);
265265
* }
266266
* }
267267
*
@@ -293,7 +293,7 @@ export class BinaryTree<K = any, V = any, R = any>
293293
if (isMapMode !== undefined) this._isMapMode = isMapMode;
294294
if (isDuplicate !== undefined) this._isDuplicate = isDuplicate;
295295
if (typeof toEntryFn === 'function') this._toEntryFn = toEntryFn;
296-
else if (toEntryFn) throw new TypeError(ERR.notAFunction('toEntryFn', 'BinaryTree'));
296+
else if (toEntryFn) raise(TypeError, ERR.notAFunction('toEntryFn', 'BinaryTree'));
297297
}
298298

299299
if (keysNodesEntriesOrRaws) this.setMany(keysNodesEntriesOrRaws);

src/data-structures/binary-tree/bst.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2639,15 +2639,13 @@ export class BST<K = any, V = any, R = any> extends BinaryTree<K, V, R> implemen
26392639
if (a instanceof Date && b instanceof Date) {
26402640
const ta = a.getTime();
26412641
const tb = b.getTime();
2642-
if (Number.isNaN(ta) || Number.isNaN(tb)) throw new TypeError(ERR.invalidDate('BST'));
2642+
if (Number.isNaN(ta) || Number.isNaN(tb)) raise(TypeError, ERR.invalidDate('BST'));
26432643
return ta > tb ? 1 : ta < tb ? -1 : 0;
26442644
}
26452645

26462646
// If keys are objects and no comparator is provided, throw an error
26472647
if (typeof a === 'object' || typeof b === 'object') {
2648-
throw new TypeError(
2649-
ERR.comparatorRequired('BST')
2650-
);
2648+
raise(TypeError, ERR.comparatorRequired('BST'));
26512649
}
26522650

26532651
// Default: keys are equal (fallback case)

src/data-structures/binary-tree/tree-map.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import type { Comparator } from '../../types';
1111
import type { TreeMapEntryCallback, TreeMapOptions, TreeMapRangeOptions, TreeMapReduceCallback } from '../../types';
1212
import { RedBlackTree } from './red-black-tree';
13-
import { ERR } from '../../common';
13+
import { ERR, raise } from '../../common';
1414

1515
/**
1616
* An ordered Map backed by a red-black tree.
@@ -64,7 +64,8 @@ export class TreeMap<K = any, V = any, R = [K, V]> implements Iterable<[K, V | u
6464
} else {
6565
// Validate entries like native Map: each item must be a 2-tuple-like value.
6666
if (!Array.isArray(item) || item.length < 2) {
67-
throw new TypeError(ERR.invalidEntry('TreeMap'));
67+
raise(TypeError, ERR.invalidEntry('TreeMap'));
68+
continue;
6869
}
6970
k = item[0] as K;
7071
v = item[1] as V | undefined;
@@ -88,7 +89,7 @@ export class TreeMap<K = any, V = any, R = [K, V]> implements Iterable<[K, V | u
8889
return (a: K, b: K): number => {
8990
if (typeof a === 'number' && typeof b === 'number') {
9091
/* istanbul ignore next -- _validateKey prevents NaN from entering the tree */
91-
if (Number.isNaN(a) || Number.isNaN(b)) throw new TypeError(ERR.invalidNaN('TreeMap'));
92+
if (Number.isNaN(a) || Number.isNaN(b)) raise(TypeError, ERR.invalidNaN('TreeMap'));
9293
const aa = Object.is(a, -0) ? 0 : a;
9394
const bb = Object.is(b, -0) ? 0 : b;
9495
return aa > bb ? 1 : aa < bb ? -1 : 0;
@@ -102,30 +103,31 @@ export class TreeMap<K = any, V = any, R = [K, V]> implements Iterable<[K, V | u
102103
const ta = a.getTime();
103104
const tb = b.getTime();
104105
/* istanbul ignore next -- _validateKey prevents invalid Date from entering the tree */
105-
if (Number.isNaN(ta) || Number.isNaN(tb)) throw new TypeError(ERR.invalidDate('TreeMap'));
106+
if (Number.isNaN(ta) || Number.isNaN(tb)) raise(TypeError, ERR.invalidDate('TreeMap'));
106107
return ta > tb ? 1 : ta < tb ? -1 : 0;
107108
}
108109

109-
throw new TypeError(ERR.comparatorRequired('TreeMap'));
110+
raise(TypeError, ERR.comparatorRequired('TreeMap'));
111+
return 0;
110112
};
111113
}
112114

113115
private _validateKey(key: K): void {
114116
if (!this.#isDefaultComparator) return;
115117

116118
if (typeof key === 'number') {
117-
if (Number.isNaN(key)) throw new TypeError(ERR.invalidNaN('TreeMap'));
119+
if (Number.isNaN(key)) raise(TypeError, ERR.invalidNaN('TreeMap'));
118120
return;
119121
}
120122

121123
if (typeof key === 'string') return;
122124

123125
if (key instanceof Date) {
124-
if (Number.isNaN(key.getTime())) throw new TypeError(ERR.invalidDate('TreeMap'));
126+
if (Number.isNaN(key.getTime())) raise(TypeError, ERR.invalidDate('TreeMap'));
125127
return;
126128
}
127129

128-
throw new TypeError(ERR.comparatorRequired('TreeMap'));
130+
raise(TypeError, ERR.comparatorRequired('TreeMap'));
129131
}
130132

131133
/**

src/data-structures/binary-tree/tree-multi-map.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import type { Comparator, TreeMultiMapOptions } from '../../types';
10-
import { ERR, Range } from '../../common';
10+
import { ERR, raise, Range } from '../../common';
1111
import { RedBlackTree, RedBlackTreeNode } from './red-black-tree';
1212
import { TreeSet } from './tree-set';
1313

@@ -91,15 +91,15 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
9191
// reuse TreeSet strict validation (same policy)
9292
// NOTE: TreeSet._validateKey is private, so we replicate the checks.
9393
if (typeof key === 'number') {
94-
if (Number.isNaN(key)) throw new TypeError(ERR.invalidNaN('TreeMultiMap'));
94+
if (Number.isNaN(key)) raise(TypeError, ERR.invalidNaN('TreeMultiMap'));
9595
return;
9696
}
9797
if (typeof key === 'string') return;
9898
if (key instanceof Date) {
99-
if (Number.isNaN(key.getTime())) throw new TypeError(ERR.invalidDate('TreeMultiMap'));
99+
if (Number.isNaN(key.getTime())) raise(TypeError, ERR.invalidDate('TreeMultiMap'));
100100
return;
101101
}
102-
throw new TypeError(ERR.comparatorRequired('TreeMultiMap'));
102+
raise(TypeError, ERR.comparatorRequired('TreeMultiMap'));
103103
}
104104

105105
/**

src/data-structures/binary-tree/tree-multi-set.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*/
1010

1111
import type { Comparator, TreeMultiSetOptions } from '../../types';
12-
import { ERR } from '../../common';
12+
import { ERR, raise } from '../../common';
1313
import { RedBlackTree } from './red-black-tree';
1414
import { TreeSet } from './tree-set';
1515

@@ -51,26 +51,26 @@ export class TreeMultiSet<K = any, R = K> implements Iterable<K> {
5151
if (!this.#isDefaultComparator) return;
5252

5353
if (typeof key === 'number') {
54-
if (Number.isNaN(key)) throw new TypeError(ERR.invalidNaN('TreeMultiSet'));
54+
if (Number.isNaN(key)) raise(TypeError, ERR.invalidNaN('TreeMultiSet'));
5555
return;
5656
}
5757

5858
if (typeof key === 'string') return;
5959

6060
if (key instanceof Date) {
61-
if (Number.isNaN(key.getTime())) throw new TypeError(ERR.invalidDate('TreeMultiSet'));
61+
if (Number.isNaN(key.getTime())) raise(TypeError, ERR.invalidDate('TreeMultiSet'));
6262
return;
6363
}
6464

65-
throw new TypeError(ERR.comparatorRequired('TreeMultiSet'));
65+
raise(TypeError, ERR.comparatorRequired('TreeMultiSet'));
6666
}
6767

6868
/**
6969
* Validates that count is a non-negative safe integer.
7070
* @remarks Time O(1), Space O(1)
7171
*/
7272
private _validateCount(n: number): void {
73-
if (!Number.isSafeInteger(n) || n < 0) throw new RangeError(ERR.invalidArgument('count must be a safe integer >= 0.', 'TreeMultiSet'));
73+
if (!Number.isSafeInteger(n) || n < 0) raise(RangeError, ERR.invalidArgument('count must be a safe integer >= 0.', 'TreeMultiSet'));
7474
}
7575

7676
/**

src/data-structures/binary-tree/tree-set.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import type { Comparator } from '../../types';
1111
import type { TreeSetElementCallback, TreeSetOptions, TreeSetRangeOptions, TreeSetReduceCallback } from '../../types';
12-
import { ERR } from '../../common';
12+
import { ERR, raise } from '../../common';
1313
import { RedBlackTree } from './red-black-tree';
1414

1515
/**
@@ -73,7 +73,7 @@ export class TreeSet<K = any, R = K> implements Iterable<K> {
7373
// numbers
7474
if (typeof a === 'number' && typeof b === 'number') {
7575
/* istanbul ignore next -- _validateKey prevents NaN from entering the tree */
76-
if (Number.isNaN(a) || Number.isNaN(b)) throw new TypeError(ERR.invalidNaN('TreeSet'));
76+
if (Number.isNaN(a) || Number.isNaN(b)) raise(TypeError, ERR.invalidNaN('TreeSet'));
7777
const aa = Object.is(a, -0) ? 0 : a;
7878
const bb = Object.is(b, -0) ? 0 : b;
7979
return aa > bb ? 1 : aa < bb ? -1 : 0;
@@ -87,11 +87,12 @@ export class TreeSet<K = any, R = K> implements Iterable<K> {
8787
const ta = a.getTime();
8888
const tb = b.getTime();
8989
/* istanbul ignore next -- _validateKey prevents invalid Date from entering the tree */
90-
if (Number.isNaN(ta) || Number.isNaN(tb)) throw new TypeError(ERR.invalidDate('TreeSet'));
90+
if (Number.isNaN(ta) || Number.isNaN(tb)) raise(TypeError, ERR.invalidDate('TreeSet'));
9191
return ta > tb ? 1 : ta < tb ? -1 : 0;
9292
}
9393

94-
throw new TypeError(ERR.comparatorRequired('TreeSet'));
94+
raise(TypeError, ERR.comparatorRequired('TreeSet'));
95+
return 0;
9596
};
9697
}
9798

@@ -274,19 +275,19 @@ export class TreeSet<K = any, R = K> implements Iterable<K> {
274275
if (!this.#isDefaultComparator) return;
275276

276277
if (typeof key === 'number') {
277-
if (Number.isNaN(key)) throw new TypeError(ERR.invalidNaN('TreeSet'));
278+
if (Number.isNaN(key)) raise(TypeError, ERR.invalidNaN('TreeSet'));
278279
return;
279280
}
280281

281282
if (typeof key === 'string') return;
282283

283284
if (key instanceof Date) {
284-
if (Number.isNaN(key.getTime())) throw new TypeError(ERR.invalidDate('TreeSet'));
285+
if (Number.isNaN(key.getTime())) raise(TypeError, ERR.invalidDate('TreeSet'));
285286
return;
286287
}
287288

288289
// Other key types should have provided a comparator, so reaching here means misuse.
289-
throw new TypeError(ERR.comparatorRequired('TreeSet'));
290+
raise(TypeError, ERR.comparatorRequired('TreeSet'));
290291
}
291292

292293
/**

src/data-structures/graph/abstract-graph.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import type { DijkstraResult, EntryCallback, GraphOptions, VertexKey } from '../../types';
1010
import { uuidV4 } from '../../utils';
11-
import { ERR } from '../../common';
11+
import { ERR, raise } from '../../common';
1212
import { IterableEntryBase } from '../base';
1313
import { IGraph } from '../../interfaces';
1414
import { Heap } from '../heap';
@@ -275,7 +275,8 @@ export abstract class AbstractGraph<
275275
const newEdge = this.createEdge(srcOrEdge, dest, weight, value);
276276
return this._addEdge(newEdge);
277277
} else {
278-
throw new TypeError(ERR.invalidArgument('dest must be a Vertex or vertex key when srcOrEdge is an Edge.', 'Graph'));
278+
raise(TypeError, ERR.invalidArgument('dest must be a Vertex or vertex key when srcOrEdge is an Edge.', 'Graph'));
279+
return false;
279280
}
280281
}
281282
}

src/data-structures/hash/hash-map.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import type {
1515
} from '../../types';
1616
import { IterableEntryBase } from '../base';
1717
import { isWeakKey, rangeCheck } from '../../utils';
18-
import { ERR } from '../../common';
18+
import { ERR, raise } from '../../common';
1919

2020
/**
2121
* Hash-based map. Supports object keys and custom hashing; offers O(1) average set/get/has.
@@ -952,9 +952,8 @@ export class LinkedHashMap<K = any, V = any, R = [K, V]> extends IterableEntryBa
952952
if (this.isEntry(rawElement)) {
953953
return rawElement;
954954
}
955-
throw new TypeError(
956-
ERR.invalidArgument('If elements do not adhere to [key, value], provide options.toEntryFn to transform raw records.', 'HashMap')
957-
);
955+
raise(TypeError, ERR.invalidArgument('If elements do not adhere to [key, value], provide options.toEntryFn to transform raw records.', 'HashMap'));
956+
return rawElement as unknown as [K, V];
958957
};
959958

960959
get toEntryFn() {

0 commit comments

Comments
 (0)