Deeply freeze object
This commit is contained in:
+14
-7
@@ -1,13 +1,17 @@
|
||||
import type { DeeplyReadonly } from './types.ts';
|
||||
|
||||
import { deepFreeze } from './misc.ts';
|
||||
|
||||
export type EventMap = Record<string, unknown>;
|
||||
|
||||
type Listener<T> = (detail: Readonly<T>) => void;
|
||||
type Listener<T> = (detail: DeeplyReadonly<T>) => void;
|
||||
|
||||
/**
|
||||
* Internally permits listeners for individual event payloads to be stored
|
||||
* in a collection typed with the union of all event payloads.
|
||||
*/
|
||||
type StoredListener<T> = {
|
||||
bivarianceHack(detail: Readonly<T>): void;
|
||||
bivarianceHack(detail: DeeplyReadonly<T>): void;
|
||||
}['bivarianceHack'];
|
||||
|
||||
/**
|
||||
@@ -79,7 +83,7 @@ export class EventEmitter<T extends EventMap> {
|
||||
* @returns An off callback that can be called to stop listening for events.
|
||||
*/
|
||||
once<K extends keyof T>(type: K, listener: Listener<T[K]>, debounceMilliseconds?: number): OffCallback {
|
||||
const wrappedListener: Listener<T[K]> = (detail: Readonly<T[K]>) => {
|
||||
const wrappedListener: Listener<T[K]> = (detail: DeeplyReadonly<T[K]>) => {
|
||||
this.off(type, listener);
|
||||
listener(detail);
|
||||
};
|
||||
@@ -151,8 +155,11 @@ export class EventEmitter<T extends EventMap> {
|
||||
const listeners = this.#listeners.get(type);
|
||||
if (!listeners) return false;
|
||||
|
||||
// Freeze the payload to make it readonly.
|
||||
const readonlyPayload = Object.freeze(payload);
|
||||
// Clone the payload to avoid freezing the original object.
|
||||
const payloadClone = structuredClone(payload);
|
||||
|
||||
// Freeze the cloned payload to make it readonly.
|
||||
const readonlyPayload = deepFreeze(payloadClone);
|
||||
|
||||
// Emit the event to all listeners.
|
||||
listeners.forEach((entry) => {
|
||||
@@ -177,7 +184,7 @@ export class EventEmitter<T extends EventMap> {
|
||||
* @param timeoutMs - The timeout in milliseconds.
|
||||
* @returns The event payload.
|
||||
*/
|
||||
async waitFor<K extends keyof T>(type: K, predicate: (payload: Readonly<T[K]>) => boolean, timeoutMs?: number): Promise<Readonly<T[K]>> {
|
||||
async waitFor<K extends keyof T>(type: K, predicate: (payload: DeeplyReadonly<T[K]>) => boolean, timeoutMs?: number): Promise<DeeplyReadonly<T[K]>> {
|
||||
// Create a promise to wait for the event to be emitted.
|
||||
return new Promise((resolve, reject) => {
|
||||
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
||||
@@ -225,7 +232,7 @@ export class EventEmitter<T extends EventMap> {
|
||||
// Create a timeout variable.
|
||||
let timeout: ReturnType<typeof setTimeout>;
|
||||
|
||||
return (detail: Readonly<T[K]>) => {
|
||||
return (detail: DeeplyReadonly<T[K]>) => {
|
||||
// If a debounce timer is already pending, clear it before scheduling the next one.
|
||||
if (timeout !== undefined) {
|
||||
clearTimeout(timeout);
|
||||
|
||||
Reference in New Issue
Block a user