@byloth/core 2.1.7 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,16 @@
1
1
  import Publisher from "../callbacks/publisher.js";
2
- import type { Subscribable } from "../types.js";
2
+ import type { Callback } from "../types.js";
3
3
 
4
4
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
5
5
  import type MapView from "./map-view.js";
6
- import type { SetViewEventsMap } from "./types.js";
6
+
7
+ interface SetViewEventsMap<T>
8
+ {
9
+ "add": (value: T) => void;
10
+ "remove": (value: T) => void;
11
+
12
+ "clear": () => void;
13
+ }
7
14
 
8
15
  /**
9
16
  * A wrapper class around the native {@link Set} class that provides additional functionality
@@ -17,7 +24,7 @@ import type { SetViewEventsMap } from "./types.js";
17
24
  * ```ts
18
25
  * const set = new SetView<number>();
19
26
  *
20
- * set.subscribe("entry:add", (value: number) => console.log(`Added ${value}`));
27
+ * set.onAdd((value: number) => console.log(`Added ${value}`));
21
28
  * set.add(42); // "Added 42"
22
29
  * ```
23
30
  *
@@ -25,7 +32,7 @@ import type { SetViewEventsMap } from "./types.js";
25
32
  *
26
33
  * @template T The type of the values in the set.
27
34
  */
28
- export default class SetView<T> extends Set<T> implements Subscribable<SetViewEventsMap<T>>
35
+ export default class SetView<T> extends Set<T>
29
36
  {
30
37
  /**
31
38
  * The internal {@link Publisher} instance used to publish events.
@@ -84,7 +91,7 @@ export default class SetView<T> extends Set<T> implements Subscribable<SetViewEv
84
91
  {
85
92
  super.add(value);
86
93
 
87
- this._publisher.publish("entry:add", value);
94
+ this._publisher.publish("add", value);
88
95
 
89
96
  return this;
90
97
  }
@@ -112,7 +119,7 @@ export default class SetView<T> extends Set<T> implements Subscribable<SetViewEv
112
119
  public override delete(value: T): boolean
113
120
  {
114
121
  const result = super.delete(value);
115
- if (result) { this._publisher.publish("entry:remove", value); }
122
+ if (result) { this._publisher.publish("remove", value); }
116
123
 
117
124
  return result;
118
125
  }
@@ -135,71 +142,77 @@ export default class SetView<T> extends Set<T> implements Subscribable<SetViewEv
135
142
  const size = this.size;
136
143
 
137
144
  super.clear();
138
- if (size > 0) { this._publisher.publish("collection:clear"); }
145
+ if (size > 0) { this._publisher.publish("clear"); }
139
146
  }
140
147
 
141
148
  /**
142
- * Subscribes to an event and adds a callback to be executed when the event is published.
149
+ * Subscribes to the `add` event of the set with a callback that will be executed when a value is added.
143
150
  *
144
151
  * ---
145
152
  *
146
153
  * @example
147
154
  * ```ts
148
- * const set = new SetView<number>();
149
- * const unsubscribe = set.subscribe("entry:add", (value: number) =>
150
- * {
151
- * if (value === 42) { unsubscribe(); }
152
- * console.log(`Added ${value}`);
153
- * });
155
+ * set.onAdd((value) => console.log(`Added ${value}`));
154
156
  *
155
157
  * set.add(2); // "Added 2"
156
158
  * set.add(42); // "Added 42"
157
- * set.add(4);
158
- * set.add(8);
159
159
  * ```
160
160
  *
161
161
  * ---
162
162
  *
163
- * @template K The key of the map containing the callback signature to subscribe.
164
- *
165
- * @param event The name of the event to subscribe to.
166
- * @param subscriber The callback to execute when the event is published.
163
+ * @param callback The callback that will be executed when a value is added to the set.
167
164
  *
168
- * @returns A function that can be used to unsubscribe the callback from the event.
165
+ * @returns A function that can be used to unsubscribe from the event.
169
166
  */
170
- public subscribe<K extends keyof SetViewEventsMap<T>>(event: K & string, subscriber: SetViewEventsMap<T>[K])
171
- : () => void
167
+ public onAdd(callback: (value: T) => void): Callback
172
168
  {
173
- return this._publisher.subscribe(event, subscriber);
169
+ return this._publisher.subscribe("add", callback);
174
170
  }
175
171
 
176
172
  /**
177
- * Unsubscribes from an event and removes a callback from being executed when the event is published.
173
+ * Subscribes to the `remove` event of the set with a callback that will be executed when a value is removed.
178
174
  *
179
175
  * ---
180
176
  *
181
177
  * @example
182
178
  * ```ts
183
- * const callback = (value: number) => console.log(`Added ${value}`);
184
- * const set = new SetView<number>();
179
+ * set.onRemove((value) => console.log(`Removed ${value}`));
185
180
  *
186
- * set.subscribe("entry:add", callback);
187
- * set.add(2); // "Added 2"
181
+ * set.delete(2); // "Removed 2"
182
+ * set.delete(42); // "Removed 42"
183
+ * ```
184
+ *
185
+ * ---
188
186
  *
189
- * set.unsubscribe("entry:add", callback);
190
- * set.add(4);
187
+ * @param callback The callback that will be executed when a value is removed from the set.
188
+ *
189
+ * @returns A function that can be used to unsubscribe from the event.
190
+ */
191
+ public onRemove(callback: (value: T) => void): Callback
192
+ {
193
+ return this._publisher.subscribe("remove", callback);
194
+ }
195
+
196
+ /**
197
+ * Subscribes to the `clear` event of the set with a callback that will be executed when the set is cleared.
198
+ *
199
+ * ---
200
+ *
201
+ * @example
202
+ * ```ts
203
+ * set.onClear(() => console.log("The set has all been cleared."));
204
+ * set.clear(); // "The set has all been cleared."
191
205
  * ```
192
206
  *
193
207
  * ---
194
208
  *
195
- * @template K The key of the map containing the callback signature to unsubscribe.
209
+ * @param callback The callback that will be executed when the set is cleared.
196
210
  *
197
- * @param event The name of the event to unsubscribe from.
198
- * @param subscriber The callback to remove from the event.
211
+ * @returns A function that can be used to unsubscribe from the event.
199
212
  */
200
- public unsubscribe<K extends keyof SetViewEventsMap<T>>(event: K & string, subscriber: SetViewEventsMap<T>[K]): void
213
+ public onClear(callback: () => void): Callback
201
214
  {
202
- this._publisher.unsubscribe(event, subscriber);
215
+ return this._publisher.subscribe("clear", callback);
203
216
  }
204
217
 
205
218
  public override readonly [Symbol.toStringTag]: string = "SetView";
@@ -1,28 +1,11 @@
1
+ import type { Callback } from "../types.js";
2
+
1
3
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
2
4
  import type MapView from "./map-view.js";
3
5
 
4
6
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
5
7
  import type SetView from "./set-view.js";
6
8
 
7
- /**
8
- * A type that represents the map of events published by a {@link MapView} object.
9
- * See also {@link SetViewEventsMap}.
10
- *
11
- * The keys of the map are the event names while the values are the callback function signatures.
12
- *
13
- * ---
14
- *
15
- * @template K The type of the keys in the map.
16
- * @template V The type of the values in the map.
17
- */
18
- export interface MapViewEventsMap<K, V>
19
- {
20
- "entry:add": (key: K, value: V) => void;
21
- "entry:remove": (key: K, value: V) => void;
22
-
23
- "collection:clear": () => void;
24
- }
25
-
26
9
  /**
27
10
  * An utility type that represents a read-only {@link MapView} object.
28
11
  * See also {@link ReadonlySetView}.
@@ -38,79 +21,65 @@ export interface MapViewEventsMap<K, V>
38
21
  export interface ReadonlyMapView<K, V> extends ReadonlyMap<K, V>
39
22
  {
40
23
  /**
41
- * Subscribes to an event and adds a callback to be executed when the event is published.
24
+ * Subscribes to the `add` event of the map with a callback that will be executed when an entry is added.
42
25
  *
43
26
  * ---
44
27
  *
45
28
  * @example
46
29
  * ```ts
47
- * const map = new MapView<string, number>();
48
- * const unsubscribe = map.subscribe("entry:add", (key: string, value: number) =>
49
- * {
50
- * if (key === "answer") { unsubscribe(); }
51
- * console.log(`Added ${key}: ${value}`);
52
- * });
30
+ * map.onAdd((key, value) => console.log(`Added ${key}: ${value}`));
53
31
  *
54
32
  * map.set("key1", 2); // "Added key1: 2"
55
33
  * map.set("answer", 42); // "Added answer: 42"
56
- * map.set("key2", 4);
57
- * map.set("key3", 8);
58
34
  * ```
59
35
  *
60
36
  * ---
61
37
  *
62
- * @template T The key of the map containing the callback signature to subscribe.
63
- *
64
- * @param event The name of the event to subscribe to.
65
- * @param callback The callback to execute when the event is published.
38
+ * @param callback The callback that will be executed when an entry is added to the map.
66
39
  *
67
- * @returns A function that can be used to unsubscribe the callback from the event.
40
+ * @returns A function that can be used to unsubscribe from the event.
68
41
  */
69
- subscribe<T extends keyof MapViewEventsMap<K, V>>(event: T, callback: MapViewEventsMap<K, V>[T]): () => void;
42
+ onAdd(callback: (key: K, value: V) => void): Callback;
70
43
 
71
44
  /**
72
- * Unsubscribes from an event and removes a callback from being executed when the event is published.
45
+ * Subscribes to the `remove` event of the map with a callback that will be executed when an entry is removed.
73
46
  *
74
47
  * ---
75
48
  *
76
49
  * @example
77
50
  * ```ts
78
- * const callback = (key: string, value: number) => console.log(`Added ${key}: ${value}`);
79
- * const map = new MapView<string, number>();
51
+ * map.onRemove((key, value) => console.log(`Removed ${key}: ${value}`));
80
52
  *
81
- * map.subscribe("entry:add", callback);
82
- * map.set("key1", 2); // "Added key1: 2"
83
- *
84
- * map.unsubscribe("entry:add", callback);
85
- * map.set("key2", 4);
53
+ * map.delete("key1"); // "Removed key1: 2"
54
+ * map.delete("answer"); // "Removed answer: 42"
86
55
  * ```
87
56
  *
88
57
  * ---
89
58
  *
90
- * @template T The key of the map containing the callback signature to unsubscribe.
59
+ * @param callback The callback that will be executed when an entry is removed from the map.
91
60
  *
92
- * @param event The name of the event to unsubscribe from.
93
- * @param callback The callback to remove from the event.
61
+ * @returns A function that can be used to unsubscribe from the event.
94
62
  */
95
- unsubscribe<T extends keyof MapViewEventsMap<K, V>>(event: T, callback: MapViewEventsMap<K, V>[T]): void;
96
- }
97
-
98
- /**
99
- * A type that represents the map of events published by a {@link SetView} object.
100
- * See also {@link MapViewEventsMap}.
101
- *
102
- * The keys of the map are the event names while the values are the callback function signatures.
103
- *
104
- * ---
105
- *
106
- * @template T The type of the values in the set.
107
- */
108
- export interface SetViewEventsMap<T>
109
- {
110
- "entry:add": (value: T) => void;
111
- "entry:remove": (value: T) => void;
63
+ onRemove(callback: (key: K, value: V) => void): Callback;
112
64
 
113
- "collection:clear": () => void;
65
+ /**
66
+ * Subscribes to the `clear` event of the map with a callback that will be executed when the map is cleared.
67
+ *
68
+ * ---
69
+ *
70
+ * @example
71
+ * ```ts
72
+ * map.onClear(() => console.log("The map has all been cleared."));
73
+ * map.clear();
74
+ * ```
75
+ *
76
+ * ---
77
+ *
78
+ * @param callback The callback that will be executed when the map is cleared.
79
+ *
80
+ * @returns A function that can be used to unsubscribe from the event.
81
+ */
82
+ onClear(callback: () => void): Callback;
114
83
  }
115
84
 
116
85
  /**
@@ -127,59 +96,63 @@ export interface SetViewEventsMap<T>
127
96
  export interface ReadonlySetView<T> extends ReadonlySet<T>
128
97
  {
129
98
  /**
130
- * Subscribes to an event and adds a callback to be executed when the event is published.
99
+ * Subscribes to the `add` event of the set with a callback that will be executed when a value is added.
131
100
  *
132
101
  * ---
133
102
  *
134
103
  * @example
135
104
  * ```ts
136
- * const set = new SetView<number>();
137
- * const unsubscribe = set.subscribe("entry:add", (value: number) =>
138
- * {
139
- * if (value === 42) { unsubscribe(); }
140
- * console.log(`Added ${value}`);
141
- * });
105
+ * set.onAdd((value) => console.log(`Added ${value}`));
142
106
  *
143
107
  * set.add(2); // "Added 2"
144
108
  * set.add(42); // "Added 42"
145
- * set.add(4);
146
- * set.add(8);
147
109
  * ```
148
110
  *
149
111
  * ---
150
112
  *
151
- * @template K The key of the map containing the callback signature to subscribe.
152
- *
153
- * @param event The name of the event to subscribe to.
154
- * @param callback The callback to execute when the event is published.
113
+ * @param callback The callback that will be executed when a value is added to the set.
155
114
  *
156
- * @returns A function that can be used to unsubscribe the callback from the event.
115
+ * @returns A function that can be used to unsubscribe from the event.
157
116
  */
158
- subscribe<K extends keyof SetViewEventsMap<T>>(event: K, callback: SetViewEventsMap<T>[K]): () => void;
117
+ onAdd(callback: (value: T) => void): Callback;
159
118
 
160
119
  /**
161
- * Unsubscribes from an event and removes a callback from being executed when the event is published.
120
+ * Subscribes to the `remove` event of the set with a callback that will be executed when a value is removed.
162
121
  *
163
122
  * ---
164
123
  *
165
124
  * @example
166
125
  * ```ts
167
- * const callback = (value: number) => console.log(`Added ${value}`);
168
- * const set = new SetView<number>();
126
+ * set.onRemove((value) => console.log(`Removed ${value}`));
169
127
  *
170
- * set.subscribe("entry:add", callback);
171
- * set.add(2); // "Added 2"
128
+ * set.delete(2); // "Removed 2"
129
+ * set.delete(42); // "Removed 42"
130
+ * ```
131
+ *
132
+ * ---
133
+ *
134
+ * @param callback The callback that will be executed when a value is removed from the set.
135
+ *
136
+ * @returns A function that can be used to unsubscribe from the event.
137
+ */
138
+ onRemove(callback: (value: T) => void): Callback;
139
+
140
+ /**
141
+ * Subscribes to the `clear` event of the set with a callback that will be executed when the set is cleared.
142
+ *
143
+ * ---
172
144
  *
173
- * set.unsubscribe("entry:add", callback);
174
- * set.add(4);
145
+ * @example
146
+ * ```ts
147
+ * set.onClear(() => console.log("The set has all been cleared."));
148
+ * set.clear(); // "The set has all been cleared."
175
149
  * ```
176
150
  *
177
151
  * ---
178
152
  *
179
- * @template K The key of the map containing the callback signature to unsubscribe.
153
+ * @param callback The callback that will be executed when the set is cleared.
180
154
  *
181
- * @param event The name of the event to unsubscribe from.
182
- * @param callback The callback to remove from the event.
155
+ * @returns A function that can be used to unsubscribe from the event.
183
156
  */
184
- unsubscribe<K extends keyof SetViewEventsMap<T>>(event: K, callback: SetViewEventsMap<T>[K]): void;
157
+ onClear(callback: () => void): Callback;
185
158
  }
@@ -212,7 +212,6 @@ export default class SmartAsyncIterator<T, R = void, N = undefined> implements A
212
212
 
213
213
  next = [yield result.value];
214
214
  }
215
-
216
215
  })();
217
216
  }
218
217
  }
@@ -232,7 +231,6 @@ export default class SmartAsyncIterator<T, R = void, N = undefined> implements A
232
231
 
233
232
  yield result.value;
234
233
  }
235
-
236
234
  })();
237
235
  }
238
236
  else
@@ -248,7 +246,6 @@ export default class SmartAsyncIterator<T, R = void, N = undefined> implements A
248
246
 
249
247
  next = [yield result.value];
250
248
  }
251
-
252
249
  })();
253
250
  }
254
251
  }
@@ -178,8 +178,7 @@ export default class JSONStorage
178
178
  * @returns The value with the specified key or the default value if the key doesn't exist.
179
179
  */
180
180
  public get<T extends JSONValue>(key: string, defaultValue?: T, persistent?: boolean): T | undefined;
181
- public get<T extends JSONValue>(key: string, defaultValue?: T, persistent = this._preferPersistence)
182
- : T | undefined
181
+ public get<T extends JSONValue>(key: string, defaultValue?: T, persistent = this._preferPersistence): T | undefined
183
182
  {
184
183
  const storage = persistent ? this._persistent : this._volatile;
185
184
 
@@ -103,9 +103,9 @@ export default class PromiseQueue extends SmartPromise<void>
103
103
  *
104
104
  * @param promise A `DeferredPromise<void, T>` instance to enqueue.
105
105
  *
106
- * @returns A {@link Promise} that resolves to the value of the enqueued promise.
106
+ * @returns A {@link SmartPromise} that resolves to the value of the enqueued promise.
107
107
  */
108
- public enqueue<T>(promise: DeferredPromise<void, T>): Promise<T>;
108
+ public enqueue<T>(promise: DeferredPromise<void, T>): SmartPromise<T>;
109
109
 
110
110
  /**
111
111
  * Enqueues a {@link DeferredPromise} into the queue with an optional timeout.
@@ -156,9 +156,9 @@ export default class PromiseQueue extends SmartPromise<void>
156
156
  *
157
157
  * @param executor A callback that returns a `MaybePromise<T>` value to enqueue.
158
158
  *
159
- * @returns A {@link Promise} that resolves to the value of the enqueued executor.
159
+ * @returns A {@link SmartPromise} that resolves to the value of the enqueued executor.
160
160
  */
161
- public enqueue<T>(executor: Callback<[], MaybePromise<T>>): Promise<T>;
161
+ public enqueue<T>(executor: Callback<[], MaybePromise<T>>): SmartPromise<T>;
162
162
 
163
163
  /**
164
164
  * Enqueues a callback that returns a {@link MaybePromise}
@@ -188,8 +188,9 @@ export default class PromiseQueue extends SmartPromise<void>
188
188
  * with a `TimeoutException` if the operation takes longer than the specified timeout.
189
189
  */
190
190
  public enqueue<T>(executor: Callback<[], MaybePromise<T>>, timeout?: number): TimedPromise<T>;
191
- public enqueue<T>(executor: DeferredPromise<void, T> | Callback<[], MaybePromise<T>>, timeout?: number)
192
- : Promise<T> | TimedPromise<T>
191
+ public enqueue<T>(
192
+ executor: DeferredPromise<void, T> | Callback<[], MaybePromise<T>>, timeout?: number
193
+ ): SmartPromise<T> | TimedPromise<T>
193
194
  {
194
195
  this._count += 1;
195
196
 
@@ -215,7 +216,7 @@ export default class PromiseQueue extends SmartPromise<void>
215
216
 
216
217
  if (timeout) { return new TimedPromise<T>(_executor, timeout); }
217
218
 
218
- return new Promise<T>(_executor);
219
+ return new SmartPromise<T>(_executor);
219
220
  }
220
221
 
221
222
  public override readonly [Symbol.toStringTag]: string = "PromiseQueue";
@@ -1,3 +1,4 @@
1
+ import type { Callback } from "../types.js";
1
2
  import type { FulfilledHandler, PromiseExecutor, RejectedHandler } from "./types.js";
2
3
 
3
4
  /**
@@ -241,11 +242,12 @@ export default class SmartPromise<T = void> implements Promise<T>
241
242
  *
242
243
  * @returns A new {@link Promise} resolved or rejected based on the callbacks.
243
244
  */
244
- public then<F = T, R = never>(onFulfilled: FulfilledHandler<T, F>, onRejected: RejectedHandler<unknown, R>)
245
- : Promise<F | R>;
246
245
  public then<F = T, R = never>(
247
- onFulfilled?: FulfilledHandler<T, F> | null,
248
- onRejected?: RejectedHandler<unknown, R> | null): Promise<F | R>
246
+ onFulfilled: FulfilledHandler<T, F>, onRejected: RejectedHandler<unknown, R>
247
+ ): Promise<F | R>;
248
+ public then<F = T, R = never>(
249
+ onFulfilled?: FulfilledHandler<T, F> | null, onRejected?: RejectedHandler<unknown, R> | null
250
+ ): Promise<F | R>
249
251
  {
250
252
  return this._promise.then(onFulfilled, onRejected);
251
253
  }
@@ -332,7 +334,7 @@ export default class SmartPromise<T = void> implements Promise<T>
332
334
  *
333
335
  * @returns A new {@link Promise} that executes the callback once the promise is settled.
334
336
  */
335
- public finally(onFinally?: (() => void) | null): Promise<T>
337
+ public finally(onFinally?: Callback | null): Promise<T>
336
338
  {
337
339
  return this._promise.finally(onFinally);
338
340
  }
@@ -1,15 +1,16 @@
1
1
  import { TimeUnit } from "../../utils/date.js";
2
2
 
3
- import Publisher from "../callbacks/publisher.js";
3
+ import type Publisher from "../callbacks/publisher.js";
4
4
  import { FatalErrorException, RangeException, RuntimeException } from "../exceptions/index.js";
5
+ import type { Callback } from "../types.js";
5
6
 
6
7
  import GameLoop from "./game-loop.js";
7
8
 
8
9
  interface ClockEventsMap
9
10
  {
10
- start: () => void;
11
- stop: () => void;
12
- tick: (elapsedTime: number) => void;
11
+ "start": () => void;
12
+ "stop": () => void;
13
+ "tick": (elapsedTime: number) => void;
13
14
  }
14
15
 
15
16
  /**
@@ -136,7 +137,7 @@ export default class Clock extends GameLoop
136
137
  *
137
138
  * @returns A function that can be used to unsubscribe from the event.
138
139
  */
139
- public onTick(callback: (elapsedTime: number) => void, tickStep = 0): () => void
140
+ public onTick(callback: (elapsedTime: number) => void, tickStep = 0): Callback
140
141
  {
141
142
  if (tickStep < 0) { throw new RangeException("The tick step must be a non-negative number."); }
142
143
  if (tickStep === 0) { return this._publisher.subscribe("tick", callback); }
@@ -1,8 +1,9 @@
1
1
  import { TimeUnit } from "../../utils/date.js";
2
2
 
3
- import Publisher from "../callbacks/publisher.js";
3
+ import type Publisher from "../callbacks/publisher.js";
4
4
  import { FatalErrorException, RangeException, RuntimeException } from "../exceptions/index.js";
5
- import { DeferredPromise, SmartPromise } from "../promises/index.js";
5
+ import type { SmartPromise } from "../promises/index.js";
6
+ import { DeferredPromise } from "../promises/index.js";
6
7
  import type { Callback } from "../types.js";
7
8
 
8
9
  import GameLoop from "./game-loop.js";
@@ -13,9 +14,6 @@ interface CountdownEventsMap
13
14
  stop: (reason: unknown) => void;
14
15
  tick: (remainingTime: number) => void;
15
16
  expire: () => void;
16
-
17
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
18
- [key: string]: Callback<any[], any>;
19
17
  }
20
18
 
21
19
  /**
@@ -210,28 +208,6 @@ export default class Countdown extends GameLoop
210
208
  this._publisher.publish("stop", reason);
211
209
  }
212
210
 
213
- /**
214
- * Subscribes to the `expire` event of the countdown.
215
- *
216
- * ---
217
- *
218
- * @example
219
- * ```ts
220
- * countdown.onExpire(() => { [...] }); // This callback will be executed once the countdown has expired.
221
- * countdown.start();
222
- * ```
223
- *
224
- * ---
225
- *
226
- * @param callback The callback that will be executed when the countdown expires.
227
- *
228
- * @returns A function that can be used to unsubscribe from the event.
229
- */
230
- public onExpire(callback: () => void): () => void
231
- {
232
- return this._publisher.subscribe("expire", callback);
233
- }
234
-
235
211
  /**
236
212
  * Subscribes to the `tick` event of the countdown.
237
213
  *
@@ -256,7 +232,7 @@ export default class Countdown extends GameLoop
256
232
  *
257
233
  * @returns A function that can be used to unsubscribe from the event.
258
234
  */
259
- public onTick(callback: (remainingTime: number) => void, tickStep = 0): () => void
235
+ public onTick(callback: (remainingTime: number) => void, tickStep = 0): Callback
260
236
  {
261
237
  if (tickStep < 0) { throw new RangeException("The tick step must be a non-negative number."); }
262
238
  if (tickStep === 0) { return this._publisher.subscribe("tick", callback); }
@@ -272,5 +248,27 @@ export default class Countdown extends GameLoop
272
248
  });
273
249
  }
274
250
 
251
+ /**
252
+ * Subscribes to the `expire` event of the countdown.
253
+ *
254
+ * ---
255
+ *
256
+ * @example
257
+ * ```ts
258
+ * countdown.onExpire(() => { [...] }); // This callback will be executed once the countdown has expired.
259
+ * countdown.start();
260
+ * ```
261
+ *
262
+ * ---
263
+ *
264
+ * @param callback The callback that will be executed when the countdown expires.
265
+ *
266
+ * @returns A function that can be used to unsubscribe from the event.
267
+ */
268
+ public onExpire(callback: Callback): Callback
269
+ {
270
+ return this._publisher.subscribe("expire", callback);
271
+ }
272
+
275
273
  public override readonly [Symbol.toStringTag]: string = "Countdown";
276
274
  }