diff --git a/package.json b/package.json index 1d4d8b5..042f2a0 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "seng-event", - "version": "2.0.0-alpha.1", + "version": "2.0.0-alpha.2", "description": "Provides Classes and utilities for dispatching and listening to events.", "main": "./index.js", "types": "./index.d.ts", diff --git a/src/index.ts b/src/index.ts index 2089295..2dc3745 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,7 +3,6 @@ import { default as _export } from './lib/EventDispatcher'; export { default as BaseEvent } from './lib/BaseEvent'; export { default as createEventType } from './lib/createEventType'; export { default as createIsomorphicEventType } from './lib/createIsomorphicEventType'; -export { default as IsomorphicBaseEvent } from './lib/IsomorphicBaseEvent'; export { default as EventPhase } from './lib/EventPhase'; export { default as EventListenerData } from './lib/EventListenerData'; export { default as CallListenerResult } from './lib/CallListenerResult'; diff --git a/src/lib/EventDispatcher.ts b/src/lib/EventDispatcher.ts index a6f722e..fffd38a 100644 --- a/src/lib/EventDispatcher.ts +++ b/src/lib/EventDispatcher.ts @@ -6,8 +6,8 @@ import AbstractEvent from './AbstractEvent'; import { EventHandlerForEvent, EventListenerMap, - ExtractEventsOfType, TypesForEvent, + ExtractEventOfType, } from './EventTypings'; /** @@ -146,7 +146,7 @@ export default class EventDispatcher< */ public addEventListener>( eventType: TType, - handler: EventHandlerForEvent>, + handler: EventHandlerForEvent>, useCapture: boolean = false, priority: number = 0, ) { @@ -171,9 +171,9 @@ export default class EventDispatcher< * is set to false by default_ * @returns {boolean} True if one or more event listeners exist */ - public hasEventListener( - eventType: TypesForEvent, - handler?: EventHandlerForEvent, + public hasEventListener>( + eventType: TType, + handler?: EventHandlerForEvent>, useCapture?: boolean, ): boolean { if (typeof handler === 'undefined') { @@ -218,8 +218,8 @@ export default class EventDispatcher< * parameter will be removed. _Please note: if no useCapture argument is provided, only * event listeners that have useCapture set to false will be removed._ */ - public removeEventListener( - eventType: TypesForEvent, + public removeEventListener>( + eventType: TType, handler: EventHandlerForEvent, useCapture: boolean = false, ): void { diff --git a/src/lib/EventTypings.ts b/src/lib/EventTypings.ts index 34b58ee..1b63874 100644 --- a/src/lib/EventTypings.ts +++ b/src/lib/EventTypings.ts @@ -6,50 +6,48 @@ export type TypesForEvent = TEvent['type']; export type EventHandlerForEvent = (event: TEvent) => any; -export type EventListenerMap = { [type: string]: Array> }; - -/** - * Returns all TEvent['type'] that contain TType - * - * example - * TEvent = { type: 'FOO'|'BAR' }|{ type: 'FOOBAR' } - * TType = 'BAR' - * ExtractEventType = 'FOO'|'BAR' - */ -type ExtractEventTypeIfContains = - TType extends TEvent['type'] ? TEvent['type'] : never; - -/** - * Returns all TEvent['type'] that contain TType - * - * example - * TEvent = { type: 'FOO'|'BAR', ... }|{ type: 'FOOBAR', ... } - * TType = 'BAR' - * ExtractEventsContainsType = { type: 'FOO'|'BAR', ... } - */ -type ExtractEventIfTypeContains = - TEvent extends { type: ExtractEventTypeIfContains } ? TEvent : never; - -/** - * If TEvent is an IsomorphicEvent (extends IsomorphicBaseEvent), narrow TEvent down to the - * IsomorphicBaseEvent with TType. - * - * Example: - * const FooEvent = createIsomorphicEventType< - * 'CREATE',{ createId: number },'CHANGE',{ createId: number, newValue: string } - * >({ CREATE: {}, CHANGE: { bubbles: true }); - * type FooEventType = InstanceType; - * - * const BarEvent = createEventType<{ barId: string }>()(['CREATE_BAR']); - * type BarEventType = InstanceType; - * - * TEvent = FooEvent | BarEvent; - * TType = 'CREATE'; - * UnpackIsomorphic = FooEvent<'CREATE'> | BarEvent - */ -type UnpackIsomorphic = - TEvent extends IsomorphicBaseEvent ? IsomorphicBaseEvent : TEvent; - -export type ExtractEventsOfType = - UnpackIsomorphic, TType>; - +export type EventListenerMap = { + [type: string]: Array>; +}; + +export type ExtractEventTypeIfContains< + TEvent extends AbstractEvent, + TType extends string +> = TType extends TEvent['type'] ? TEvent['type'] : never; + +export type ExtractEventIfTypeContains< + TEvent extends AbstractEvent, + TType extends string +> = TEvent extends { type: ExtractEventTypeIfContains } ? TEvent : never; + +export type UnpackIsomorphic< + TEvent extends AbstractEvent, + TType extends string +> = TEvent extends IsomorphicBaseEvent + ? IsomorphicBaseEvent + : TEvent; + +export type ExtractEventOfType< + TEvent extends AbstractEvent, + TType extends string +> = UnpackIsomorphic, TType>; + +// prettier-ignore +export type DataForIsomorphicEvent< + T extends string, + TTypes extends Array, + TDataTypes extends Array +> = + T extends TTypes[0] ? TDataTypes[0] + : T extends TTypes[1] ? TDataTypes[1] + : T extends TTypes[2] ? TDataTypes[2] + : T extends TTypes[3] ? TDataTypes[3] + : T extends TTypes[4] ? TDataTypes[4] + : T extends TTypes[5] ? TDataTypes[5] + : T extends TTypes[6] ? TDataTypes[6] + : T extends TTypes[7] ? TDataTypes[7] + : T extends TTypes[8] ? TDataTypes[8] + : T extends TTypes[9] ? TDataTypes[9] + : T extends TTypes[10] ? TDataTypes[10] + : T extends TTypes[11] ? TDataTypes[11] + : never; diff --git a/src/lib/IsomorphicBaseEvent.ts b/src/lib/IsomorphicBaseEvent.ts index 559f814..0a5ff70 100644 --- a/src/lib/IsomorphicBaseEvent.ts +++ b/src/lib/IsomorphicBaseEvent.ts @@ -1,51 +1,28 @@ import BaseEvent from './BaseEvent'; +import { DataForIsomorphicEvent } from './EventTypings'; -export interface IEventOptions { +export interface EventOptions { bubbles?: boolean; cancelable?: boolean; setTimeStamp?: boolean; } -export type IEventOptionsMap = { - [P in TTypes]: IEventOptions; -}; - -export type TypeMap = { [P in TType]: P }; - -interface IAnyTypeMap { - [type: string]: string; -} - -export type TupleType = [T, T, T, T, T, T]; - -export type DataForIsomorphicEvent< - T extends string, - TTypes extends TupleType, - TDataTypes extends TupleType -> = - T extends TTypes[0] ? TDataTypes[0] : - T extends TTypes[1] ? TDataTypes[1] : - T extends TTypes[2] ? TDataTypes[2] : - T extends TTypes[3] ? TDataTypes[3] : - T extends TTypes[4] ? TDataTypes[4] : - T extends TTypes[5] ? TDataTypes[5] : never; +export type EventOptionsMap = { [T in TTypes]: EventOptions }; class IsomorphicBaseEvent< - TTypes extends TupleType, - TDataTypes extends TupleType, - TType extends TTypes[number], + TTypes extends Array, + TDataTypes extends Array, + TType extends TTypes[number] > extends BaseEvent, TType> { - public static types:IAnyTypeMap; - - public type:TType; - public data:DataForIsomorphicEvent; + public type: TType; + public data: DataForIsomorphicEvent; - private typeOptions:IEventOptionsMap; + private typeOptions: EventOptionsMap; constructor( type: TType, data: DataForIsomorphicEvent, - typeOptions:IEventOptionsMap + typeOptions: EventOptionsMap, ) { const { bubbles, cancelable, setTimeStamp } = typeOptions[type]; super(type, data, bubbles, cancelable, setTimeStamp); diff --git a/src/lib/createEventType.ts b/src/lib/createEventType.ts index 6ffcf85..bdfc5f3 100644 --- a/src/lib/createEventType.ts +++ b/src/lib/createEventType.ts @@ -8,178 +8,9 @@ interface IEventTypeClass { } function createEventType(bubbles?: boolean, cancelable?: boolean, setTimeStamp?: boolean) { - /* - * Overload signatures generated by running the following code: - * console.log(new Array(15).fill(0).map((_, i) => `function createEventTypeHelper<${new Array(i + 1).fill(0).map((_, index) => `T${index + 1} extends string`).join(', ')}>(\ntypes: [${new Array(i + 1).fill(0).map((_, index) => `T${index + 1}`).join(', ')}],\n): IEventTypeClass `T${index + 1}`).join(' | ')}>;`).join('\n')); - */ - function createEventTypeHelper(types: [T1]): IEventTypeClass; - function createEventTypeHelper( - types: [T1, T2], - ): IEventTypeClass; - function createEventTypeHelper( - types: [T1, T2, T3], - ): IEventTypeClass; - function createEventTypeHelper< - T1 extends string, - T2 extends string, - T3 extends string, - T4 extends string - >(types: [T1, T2, T3, T4]): IEventTypeClass; - function createEventTypeHelper< - T1 extends string, - T2 extends string, - T3 extends string, - T4 extends string, - T5 extends string - >(types: [T1, T2, T3, T4, T5]): IEventTypeClass; - function createEventTypeHelper< - T1 extends string, - T2 extends string, - T3 extends string, - T4 extends string, - T5 extends string, - T6 extends string - >(types: [T1, T2, T3, T4, T5, T6]): IEventTypeClass; - function createEventTypeHelper< - T1 extends string, - T2 extends string, - T3 extends string, - T4 extends string, - T5 extends string, - T6 extends string, - T7 extends string - >(types: [T1, T2, T3, T4, T5, T6, T7]): IEventTypeClass; - function createEventTypeHelper< - T1 extends string, - T2 extends string, - T3 extends string, - T4 extends string, - T5 extends string, - T6 extends string, - T7 extends string, - T8 extends string - >( - types: [T1, T2, T3, T4, T5, T6, T7, T8], - ): IEventTypeClass; - function createEventTypeHelper< - T1 extends string, - T2 extends string, - T3 extends string, - T4 extends string, - T5 extends string, - T6 extends string, - T7 extends string, - T8 extends string, - T9 extends string - >( - types: [T1, T2, T3, T4, T5, T6, T7, T8, T9], - ): IEventTypeClass; - function createEventTypeHelper< - T1 extends string, - T2 extends string, - T3 extends string, - T4 extends string, - T5 extends string, - T6 extends string, - T7 extends string, - T8 extends string, - T9 extends string, - T10 extends string - >( - types: [T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], - ): IEventTypeClass; - function createEventTypeHelper< - T1 extends string, - T2 extends string, - T3 extends string, - T4 extends string, - T5 extends string, - T6 extends string, - T7 extends string, - T8 extends string, - T9 extends string, - T10 extends string, - T11 extends string - >( - types: [T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], - ): IEventTypeClass; - function createEventTypeHelper< - T1 extends string, - T2 extends string, - T3 extends string, - T4 extends string, - T5 extends string, - T6 extends string, - T7 extends string, - T8 extends string, - T9 extends string, - T10 extends string, - T11 extends string, - T12 extends string - >( - types: [T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], - ): IEventTypeClass; - function createEventTypeHelper< - T1 extends string, - T2 extends string, - T3 extends string, - T4 extends string, - T5 extends string, - T6 extends string, - T7 extends string, - T8 extends string, - T9 extends string, - T10 extends string, - T11 extends string, - T12 extends string, - T13 extends string - >( - types: [T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], - ): IEventTypeClass; - function createEventTypeHelper< - T1 extends string, - T2 extends string, - T3 extends string, - T4 extends string, - T5 extends string, - T6 extends string, - T7 extends string, - T8 extends string, - T9 extends string, - T10 extends string, - T11 extends string, - T12 extends string, - T13 extends string, - T14 extends string - >( - types: [T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], - ): IEventTypeClass< - TData, - T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9 | T10 | T11 | T12 | T13 | T14 - >; - function createEventTypeHelper< - T1 extends string, - T2 extends string, - T3 extends string, - T4 extends string, - T5 extends string, - T6 extends string, - T7 extends string, - T8 extends string, - T9 extends string, - T10 extends string, - T11 extends string, - T12 extends string, - T13 extends string, - T14 extends string, - T15 extends string - >( - types: [T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], - ): IEventTypeClass< - TData, - T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9 | T10 | T11 | T12 | T13 | T14 | T15 - >; - function createEventTypeHelper(types: Array) { + function createEventTypeHelper>( + ...types: TEventTypes + ): IEventTypeClass { class EventType extends BaseEvent { public static types = types.reduce>((result, t) => ({ ...result, t }), {}); diff --git a/src/lib/createIsomorphicEventType.ts b/src/lib/createIsomorphicEventType.ts index 5bb29b1..0fad7a6 100644 --- a/src/lib/createIsomorphicEventType.ts +++ b/src/lib/createIsomorphicEventType.ts @@ -1,48 +1,39 @@ -import { - IEventOptionsMap, - default as IsomorphicBaseEvent, - TypeMap, - DataForIsomorphicEvent, -} from './IsomorphicBaseEvent'; +import { DataForIsomorphicEvent } from './EventTypings'; +import IsomorphicBaseEvent, { EventOptions, EventOptionsMap } from './IsomorphicBaseEvent'; -function objectKeys(o: T): Array { - return Object.keys(o); -} - -function createIsomorphicEventType< - T1 extends string = never, - TData1 = never, - T2 extends string = never, - TData2 = never, - T3 extends string = never, - TData3 = never, - T4 extends string = never, - TData4 = never, - T5 extends string = never, - TData5 = never, - T6 extends string = never, - TData6 = never ->(typeOptions: IEventOptionsMap) { - type TTypesTuple = [T1, T2, T3, T4, T5, T6]; - type EventTypes = T1 | T2 | T3 | T4 | T5 | T6; - type TDataTuple = [TData1, TData2, TData3, TData4, TData5, TData6]; +type TypeMap = { [P in TType]: P }; - class IsomorphicEventType extends IsomorphicBaseEvent< - TTypesTuple, - TDataTuple, - TType - > { - public static types: TypeMap = objectKeys(typeOptions).reduce>( - (result: any, t) => ({ ...result, t }), - {} as any, - ); +function createIsomorphicEventType>() { + return function isomorphicStringHelper< + TTypesTuple extends Array & { length: TDataTuple['length'] } + >(...types: TTypesTuple) { + return function isomorphicOptionsHelper(...typeOptionsArray: Array) { + const typeOptions = types.reduce( + (typeOptionsMap, type, index) => { + typeOptionsMap[type] = { + cancelable: false, + bubbles: false, + setTimestamp: false, + ...(typeOptionsArray[index] || {}), + }; + return typeOptionsMap; + }, + {} as any, + ) as EventOptionsMap; - constructor(type: TType, data: DataForIsomorphicEvent) { - super(type, data, typeOptions); - } - } + return class IsomorphicEventType< + TType extends TTypesTuple[number] = TTypesTuple[number] + > extends IsomorphicBaseEvent { + public static types: TypeMap = types.reduce< + TypeMap + >((result: any, t) => ({ ...result, t }), {} as any); - return IsomorphicEventType; + constructor(type: TType, data: DataForIsomorphicEvent) { + super(type, data, typeOptions); + } + }; + }; + }; } export default createIsomorphicEventType; diff --git a/test/createIsomorphicEventType.spec.ts b/test/createIsomorphicEventType.spec.ts new file mode 100644 index 0000000..420d74b --- /dev/null +++ b/test/createIsomorphicEventType.spec.ts @@ -0,0 +1,38 @@ +import createIsomorphicEventType from '../src/lib/createIsomorphicEventType'; + +describe('createIsomorphicEventType', () => { + it('should use passed event config', () => { + interface PlayerEventData { + playerId: number; + } + + interface PlayerUpdateEventData extends PlayerEventData { + currentTime: number; + } + + class IsoPlayerEvent extends createIsomorphicEventType< + [PlayerEventData, PlayerEventData, PlayerUpdateEventData] + >()('PLAY', 'STOP', 'UPDATE')({ cancelable: true }) {} + + const playEvent = new IsoPlayerEvent('PLAY', { playerId: 1 }); + expect(playEvent.cancelable).toBe(true); + }); + it('should use defaults for undefined event config', () => { + interface PlayerEventData { + playerId: number; + } + + interface PlayerUpdateEventData extends PlayerEventData { + currentTime: number; + } + + class IsoPlayerEvent extends createIsomorphicEventType< + [PlayerEventData, PlayerEventData, PlayerUpdateEventData] + >()('PLAY', 'STOP', 'UPDATE')({ cancelable: true }) {} + + const updateEvent = new IsoPlayerEvent('UPDATE', { playerId: 1, currentTime: 10 }); + expect(updateEvent.cancelable).toBe(false); + expect(updateEvent.bubbles).toBe(false); + expect(updateEvent.timeStamp).toBe(0); + }); +}); diff --git a/test/types/tests.ts b/test/types/tests.ts index 0d36ec4..381349b 100644 --- a/test/types/tests.ts +++ b/test/types/tests.ts @@ -1,11 +1,47 @@ import createEventType from '../../src/lib/createEventType'; +import createIsomorphicEventType from '../../src/lib/createIsomorphicEventType'; +import EventDispatcher from '../../src/lib/EventDispatcher'; interface PlayerEventData { - id: number; + playerId: number; } +interface PlayerUpdateEventData extends PlayerEventData { + currentTime: number; +} + +class PlayerEvent extends createEventType()('PLAY', 'STOP') {} -const PlayerEvent = createEventType()(['PLAY', 'STOP']); +// $ExpectType "PLAY" +PlayerEvent.types.PLAY; // $ExpectError Argument of type '"someMadeUpString"' is not assignable -const myEvent = new PlayerEvent('someMadeUpString', { playerId: 5 }); +new PlayerEvent('someMadeUpString', { playerId: 5 }); + +// $ExpectType PlayerEvent +new PlayerEvent('PLAY', { playerId: 5 }); + +class IsoPlayerEvent extends createIsomorphicEventType< + [PlayerEventData, PlayerEventData, PlayerUpdateEventData] +>()('PLAY', 'STOP', 'UPDATE')({ cancelable: true }) {} + +// $ExpectType "UPDATE" +IsoPlayerEvent.types.UPDATE; + +type IsoPlayerEventType = InstanceType; + +class Player extends EventDispatcher {} + +const player = new Player(); +player.addEventListener('PLAY', e => { + // $ExpectType number + e.data.playerId; + // $ExpectError Property 'currentTime' does not exist + e.data.currentTime; +}); + +player.addEventListener('UPDATE', e => { + // $ExpectType number + e.data.playerId; + e.data.currentTime; +});