update: add a physic scenne

This commit is contained in:
Tom Boullay
2026-04-17 10:48:18 +02:00
parent 1d4f223c35
commit 5111f2e558
22 changed files with 2050 additions and 216 deletions
+33 -13
View File
@@ -1,18 +1,36 @@
type Listener<TPayload> = (payload: TPayload) => void;
// TypeScript cannot narrow mapped-type indexed access by a generic key TKey
// (microsoft/TypeScript#30581). The helper below encapsulates the one necessary
// cast so the rest of the class stays cast-free.
type ListenerMap<TEvents extends Record<string, unknown>> = {
[TKey in keyof TEvents]?: Set<Listener<TEvents[TKey]>>;
};
function getListeners<
TEvents extends Record<string, unknown>,
TKey extends keyof TEvents,
>(
map: ListenerMap<TEvents>,
key: TKey,
): Set<Listener<TEvents[TKey]>> | undefined {
return map[key] as Set<Listener<TEvents[TKey]>> | undefined;
}
export class EventEmitter<TEvents extends Record<string, unknown>> {
private readonly listeners = new Map<
keyof TEvents,
Set<Listener<TEvents[keyof TEvents]>>
>();
private readonly listeners: ListenerMap<TEvents> = {};
on<TKey extends keyof TEvents>(
event: TKey,
listener: Listener<TEvents[TKey]>,
): () => void {
const currentListeners = this.listeners.get(event) ?? new Set();
currentListeners.add(listener as Listener<TEvents[keyof TEvents]>);
this.listeners.set(event, currentListeners);
const existing = getListeners(this.listeners, event);
if (existing) {
existing.add(listener);
} else {
this.listeners[event] = new Set([listener]) as ListenerMap<TEvents>[TKey];
}
return () => {
this.off(event, listener);
@@ -23,32 +41,34 @@ export class EventEmitter<TEvents extends Record<string, unknown>> {
event: TKey,
listener: Listener<TEvents[TKey]>,
): void {
const currentListeners = this.listeners.get(event);
const currentListeners = getListeners(this.listeners, event);
if (!currentListeners) {
return;
}
currentListeners.delete(listener as Listener<TEvents[keyof TEvents]>);
currentListeners.delete(listener);
if (currentListeners.size === 0) {
this.listeners.delete(event);
delete this.listeners[event];
}
}
emit<TKey extends keyof TEvents>(event: TKey, payload: TEvents[TKey]): void {
const currentListeners = this.listeners.get(event);
const currentListeners = getListeners(this.listeners, event);
if (!currentListeners) {
return;
}
currentListeners.forEach((listener) => {
listener(payload as TEvents[keyof TEvents]);
listener(payload);
});
}
clear(): void {
this.listeners.clear();
for (const key of Object.keys(this.listeners) as (keyof TEvents)[]) {
delete this.listeners[key];
}
}
}