Skip to content

Commit

Permalink
Improve syntax and tests for isomorphic events
Browse files Browse the repository at this point in the history
  • Loading branch information
flut1 committed Feb 17, 2019
1 parent b8b75ce commit 06b96c2
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 305 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
14 changes: 7 additions & 7 deletions src/lib/EventDispatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import AbstractEvent from './AbstractEvent';
import {
EventHandlerForEvent,
EventListenerMap,
ExtractEventsOfType,
TypesForEvent,
ExtractEventOfType,
} from './EventTypings';

/**
Expand Down Expand Up @@ -146,7 +146,7 @@ export default class EventDispatcher<
*/
public addEventListener<TType extends TypesForEvent<TEvent>>(
eventType: TType,
handler: EventHandlerForEvent<ExtractEventsOfType<TEvent, TType>>,
handler: EventHandlerForEvent<ExtractEventOfType<TEvent, TType>>,
useCapture: boolean = false,
priority: number = 0,
) {
Expand All @@ -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<TEvent>,
handler?: EventHandlerForEvent<TEvent>,
public hasEventListener<TType extends TypesForEvent<TEvent>>(
eventType: TType,
handler?: EventHandlerForEvent<ExtractEventOfType<TEvent, TType>>,
useCapture?: boolean,
): boolean {
if (typeof handler === 'undefined') {
Expand Down Expand Up @@ -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<TEvent>,
public removeEventListener<TType extends TypesForEvent<TEvent>>(
eventType: TType,
handler: EventHandlerForEvent<TEvent>,
useCapture: boolean = false,
): void {
Expand Down
92 changes: 45 additions & 47 deletions src/lib/EventTypings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,50 +6,48 @@ export type TypesForEvent<TEvent extends AbstractEvent> = TEvent['type'];

export type EventHandlerForEvent<TEvent extends AbstractEvent> = (event: TEvent) => any;

export type EventListenerMap<TEvent extends AbstractEvent> = { [type: string]: Array<EventListenerData<TEvent>> };

/**
* Returns all TEvent['type'] that contain TType
*
* example
* TEvent = { type: 'FOO'|'BAR' }|{ type: 'FOOBAR' }
* TType = 'BAR'
* ExtractEventType<TEvent, TType> = 'FOO'|'BAR'
*/
type ExtractEventTypeIfContains<TEvent extends AbstractEvent, TType extends string> =
TType extends TEvent['type'] ? TEvent['type'] : never;

/**
* Returns all TEvent['type'] that contain TType
*
* example
* TEvent = { type: 'FOO'|'BAR', ... }|{ type: 'FOOBAR', ... }
* TType = 'BAR'
* ExtractEventsContainsType<TEvent, TType> = { type: 'FOO'|'BAR', ... }
*/
type ExtractEventIfTypeContains<TEvent extends AbstractEvent, TType extends string> =
TEvent extends { type: ExtractEventTypeIfContains<TEvent, TType> } ? 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<typeof FooEvent>;
*
* const BarEvent = createEventType<{ barId: string }>()(['CREATE_BAR']);
* type BarEventType = InstanceType<typeof BarEvent>;
*
* TEvent = FooEvent | BarEvent;
* TType = 'CREATE';
* UnpackIsomorphic<TEvent, TType> = FooEvent<'CREATE'> | BarEvent
*/
type UnpackIsomorphic<TEvent extends AbstractEvent, TType extends string> =
TEvent extends IsomorphicBaseEvent<infer T, infer U, any> ? IsomorphicBaseEvent<T, U, TType> : TEvent;

export type ExtractEventsOfType<TEvent extends AbstractEvent, TType extends string> =
UnpackIsomorphic<ExtractEventIfTypeContains<TEvent, TType>, TType>;

export type EventListenerMap<TEvent extends AbstractEvent> = {
[type: string]: Array<EventListenerData<TEvent>>;
};

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, TType> } ? TEvent : never;

export type UnpackIsomorphic<
TEvent extends AbstractEvent,
TType extends string
> = TEvent extends IsomorphicBaseEvent<infer T, infer U, any>
? IsomorphicBaseEvent<T, U, TType>
: TEvent;

export type ExtractEventOfType<
TEvent extends AbstractEvent,
TType extends string
> = UnpackIsomorphic<ExtractEventIfTypeContains<TEvent, TType>, TType>;

// prettier-ignore
export type DataForIsomorphicEvent<
T extends string,
TTypes extends Array<string>,
TDataTypes extends Array<any>
> =
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;
43 changes: 10 additions & 33 deletions src/lib/IsomorphicBaseEvent.ts
Original file line number Diff line number Diff line change
@@ -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<TTypes extends string> = {
[P in TTypes]: IEventOptions;
};

export type TypeMap<TType extends string> = { [P in TType]: P };

interface IAnyTypeMap {
[type: string]: string;
}

export type TupleType<T> = [T, T, T, T, T, T];

export type DataForIsomorphicEvent<
T extends string,
TTypes extends TupleType<string>,
TDataTypes extends TupleType<any>
> =
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<TTypes extends string> = { [T in TTypes]: EventOptions };

class IsomorphicBaseEvent<
TTypes extends TupleType<string>,
TDataTypes extends TupleType<any>,
TType extends TTypes[number],
TTypes extends Array<string>,
TDataTypes extends Array<any>,
TType extends TTypes[number]
> extends BaseEvent<DataForIsomorphicEvent<TType, TTypes, TDataTypes>, TType> {
public static types:IAnyTypeMap;

public type:TType;
public data:DataForIsomorphicEvent<TType, TTypes, TDataTypes>;
public type: TType;
public data: DataForIsomorphicEvent<TType, TTypes, TDataTypes>;

private typeOptions:IEventOptionsMap<TTypes[number]>;
private typeOptions: EventOptionsMap<TTypes[number]>;

constructor(
type: TType,
data: DataForIsomorphicEvent<TType, TTypes, TDataTypes>,
typeOptions:IEventOptionsMap<TTypes[number]>
typeOptions: EventOptionsMap<TTypes[number]>,
) {
const { bubbles, cancelable, setTimeStamp } = typeOptions[type];
super(type, data, bubbles, cancelable, setTimeStamp);
Expand Down
175 changes: 3 additions & 172 deletions src/lib/createEventType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,178 +8,9 @@ interface IEventTypeClass<TData, TType extends string> {
}

function createEventType<TData>(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<TData, ${new Array(i + 1).fill(0).map((_, index) => `T${index + 1}`).join(' | ')}>;`).join('\n'));
*/
function createEventTypeHelper<T1 extends string>(types: [T1]): IEventTypeClass<TData, T1>;
function createEventTypeHelper<T1 extends string, T2 extends string>(
types: [T1, T2],
): IEventTypeClass<TData, T1 | T2>;
function createEventTypeHelper<T1 extends string, T2 extends string, T3 extends string>(
types: [T1, T2, T3],
): IEventTypeClass<TData, T1 | T2 | T3>;
function createEventTypeHelper<
T1 extends string,
T2 extends string,
T3 extends string,
T4 extends string
>(types: [T1, T2, T3, T4]): IEventTypeClass<TData, T1 | T2 | T3 | T4>;
function createEventTypeHelper<
T1 extends string,
T2 extends string,
T3 extends string,
T4 extends string,
T5 extends string
>(types: [T1, T2, T3, T4, T5]): IEventTypeClass<TData, T1 | T2 | T3 | T4 | T5>;
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<TData, T1 | T2 | T3 | T4 | T5 | T6>;
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<TData, T1 | T2 | T3 | T4 | T5 | T6 | T7>;
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<TData, T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8>;
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<TData, T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9>;
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<TData, T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9 | T10>;
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<TData, T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9 | T10 | T11>;
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<TData, T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9 | T10 | T11 | T12>;
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<TData, T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9 | T10 | T11 | T12 | T13>;
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<string>) {
function createEventTypeHelper<TEventTypes extends Array<string>>(
...types: TEventTypes
): IEventTypeClass<TData, TEventTypes[number]> {
class EventType extends BaseEvent<any, string> {
public static types = types.reduce<TypeMap<string>>((result, t) => ({ ...result, t }), {});

Expand Down
Loading

0 comments on commit 06b96c2

Please sign in to comment.