Skip to content

Commit c428c94

Browse files
committed
Improve EventEmitter types
1 parent 8939cf2 commit c428c94

File tree

7 files changed

+39
-37
lines changed

7 files changed

+39
-37
lines changed

packages/react-form-with-constraints/src/EventEmitter.test.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ function toMap(object: object) {
66
return new Map(Object.entries(object));
77
}
88

9-
const listener10 = jest.fn().mockReturnValue(10);
10-
const listener11 = jest.fn().mockReturnValue(11);
11-
const listener20 = jest.fn().mockReturnValue(20);
9+
const listener10 = jest.fn<10, any[]>().mockReturnValue(10);
10+
const listener11 = jest.fn<11, any[]>().mockReturnValue(11);
11+
const listener20 = jest.fn<20, any[]>().mockReturnValue(20);
1212

1313
beforeEach(() => {
1414
listener10.mockClear();
@@ -17,7 +17,7 @@ beforeEach(() => {
1717
});
1818

1919
test('addListener', () => {
20-
const eventEmitter = new EventEmitter();
20+
const eventEmitter = new EventEmitter<[], number>();
2121
expect(eventEmitter.listeners).toEqual(toMap({}));
2222

2323
eventEmitter.addListener('event1', listener10);
@@ -54,7 +54,7 @@ test('addListener', () => {
5454

5555
describe('emitSync()', () => {
5656
test('with and without args', () => {
57-
const eventEmitter = new EventEmitter();
57+
const eventEmitter = new EventEmitter<[] | [string] | [string, string], number>();
5858
eventEmitter.addListener('event1', listener10);
5959
eventEmitter.addListener('event1', listener11);
6060
eventEmitter.addListener('event2', listener20);
@@ -92,7 +92,7 @@ describe('emitSync()', () => {
9292
});
9393

9494
test('unknown event', () => {
95-
const eventEmitter = new EventEmitter();
95+
const eventEmitter = new EventEmitter<[], number>();
9696
eventEmitter.addListener('event1', listener10);
9797

9898
// Assert disabled: mess with the unit tests
@@ -103,7 +103,7 @@ describe('emitSync()', () => {
103103
});
104104

105105
test('no listener', () => {
106-
const eventEmitter = new EventEmitter();
106+
const eventEmitter = new EventEmitter<[], number>();
107107
eventEmitter.addListener('event1', listener10);
108108
clearArray(eventEmitter.listeners.get('event1')!);
109109

@@ -119,14 +119,14 @@ describe('emitSync()', () => {
119119
});
120120

121121
test('emitAsync()', async () => {
122-
const asyncListener10 = jest.fn().mockResolvedValue(10);
123-
const asyncListener11 = jest.fn().mockResolvedValue(11);
124-
const asyncListener20 = jest.fn().mockResolvedValue(20);
122+
const asyncListener10 = jest.fn<Promise<10>, any[]>().mockResolvedValue(10);
123+
const asyncListener11 = jest.fn<Promise<11>, any[]>().mockResolvedValue(11);
124+
const asyncListener20 = jest.fn<Promise<20>, any[]>().mockResolvedValue(20);
125125

126126
let isFulfilled = false;
127127
Promise.all([asyncListener10, asyncListener11, asyncListener20]).then(() => (isFulfilled = true));
128128

129-
const eventEmitter = new EventEmitter();
129+
const eventEmitter = new EventEmitter<[string], number>();
130130
eventEmitter.addListener('event1', asyncListener10);
131131
eventEmitter.addListener('event1', asyncListener11);
132132
eventEmitter.addListener('event2', asyncListener20);
@@ -145,7 +145,7 @@ test('emitAsync()', async () => {
145145

146146
describe('removeListener()', () => {
147147
test('known event', () => {
148-
const eventEmitter = new EventEmitter();
148+
const eventEmitter = new EventEmitter<[], number>();
149149
eventEmitter.addListener('event1', listener10);
150150
eventEmitter.addListener('event1', listener11);
151151
expect(eventEmitter.listeners).toEqual(
@@ -166,7 +166,7 @@ describe('removeListener()', () => {
166166
});
167167

168168
test('unknown event', () => {
169-
const eventEmitter = new EventEmitter();
169+
const eventEmitter = new EventEmitter<[], number>();
170170
eventEmitter.addListener('event1', listener10);
171171
expect(eventEmitter.listeners).toEqual(
172172
toMap({
@@ -190,7 +190,7 @@ describe('removeListener()', () => {
190190
test('no listener', () => {
191191
const unknownListener = jest.fn();
192192

193-
const eventEmitter = new EventEmitter();
193+
const eventEmitter = new EventEmitter<[], number>();
194194
eventEmitter.addListener('event1', listener10);
195195
expect(eventEmitter.listeners).toEqual(
196196
toMap({
@@ -212,7 +212,7 @@ describe('removeListener()', () => {
212212
});
213213

214214
test('multiple listeners', () => {
215-
const eventEmitter = new EventEmitter();
215+
const eventEmitter = new EventEmitter<[], number>();
216216
eventEmitter.addListener('event1', listener10);
217217
eventEmitter.addListener('event1', listener11);
218218

packages/react-form-with-constraints/src/EventEmitter.ts

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,18 @@
1-
// FIXME
2-
// See [Thoughts about variadic generics?](https://github.com/Microsoft/TypeScript/issues/1773)
3-
// See [Proposal: Variadic Kinds -- Give specific types to variadic functions](https://github.com/Microsoft/TypeScript/issues/5453)
4-
type Args = any[];
1+
// FIXME [Proposal: Variadic Kinds -- Give specific types to variadic functions](https://github.com/Microsoft/TypeScript/issues/5453)
52

6-
type Listener<ListenerReturnType = void> = (
7-
...args: Args
8-
) => ListenerReturnType | Promise<ListenerReturnType>;
3+
type Listener<Args extends any[], ReturnType> = (...args: Args) => ReturnType | Promise<ReturnType>;
94

10-
export class EventEmitter<ListenerReturnType = void> {
11-
listeners = new Map<string, Listener<ListenerReturnType>[]>();
5+
export class EventEmitter<ListenerArgs extends any[], ListenerReturnType> {
6+
listeners = new Map<string, Listener<ListenerArgs, ListenerReturnType>[]>();
127

13-
emitSync(eventName: string, ...args: Args) {
8+
emitSync(eventName: string, ...args: ListenerArgs) {
149
const listeners = this.getListeners(eventName);
1510
const ret = new Array<ListenerReturnType>();
1611
listeners.forEach(listener => ret.push(listener(...args) as ListenerReturnType));
1712
return ret;
1813
}
1914

20-
async emitAsync(eventName: string, ...args: Args) {
15+
async emitAsync(eventName: string, ...args: ListenerArgs) {
2116
const listeners = this.getListeners(eventName);
2217
const ret = new Array<ListenerReturnType>();
2318
for (let i = 0; i < listeners.length; i++) {
@@ -46,7 +41,7 @@ export class EventEmitter<ListenerReturnType = void> {
4641
return [];
4742
}
4843

49-
addListener(eventName: string, listener: Listener<ListenerReturnType>) {
44+
addListener(eventName: string, listener: Listener<ListenerArgs, ListenerReturnType>) {
5045
if (!this.listeners.has(eventName)) this.listeners.set(eventName, []);
5146
const listeners = this.listeners.get(eventName)!;
5247
console.assert(
@@ -60,7 +55,7 @@ export class EventEmitter<ListenerReturnType = void> {
6055
// "removeListener will remove, at most, one instance of a listener from the listener array.
6156
// If any single listener has been added multiple times to the listener array for the specified eventName,
6257
// then removeListener must be called multiple times to remove each instance."
63-
removeListener(eventName: string, listener: Listener<ListenerReturnType>) {
58+
removeListener(eventName: string, listener: Listener<ListenerArgs, ListenerReturnType>) {
6459
const listeners = this.listeners.get(eventName)!;
6560
console.assert(listeners !== undefined, `Unknown event '${eventName}'`);
6661

packages/react-form-with-constraints/src/FieldsStore.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export enum FieldEvent {
66
Removed = 'FIELD_REMOVED'
77
}
88

9-
export class FieldsStore extends EventEmitter {
9+
export class FieldsStore extends EventEmitter<[Field | string], void> {
1010
readonly fields = new Array<Field>();
1111

1212
getField(fieldName: string) {

packages/react-form-with-constraints/src/withFieldDidResetEventEmitter.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ export const FieldDidResetEvent = 'FIELD_DID_RESET_EVENT';
66

77
// See [TypeScript 2.2 Support for Mix-in classes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html)
88
export function withFieldDidResetEventEmitter<TBase extends Constructor<{}>>(Base: TBase) {
9-
type Listener = (field: Field) => void;
9+
type ListenerArg = Field;
10+
type ListenerReturnType = void;
11+
type Listener = (field: ListenerArg) => ListenerReturnType;
1012

1113
return class ResetFieldEvenEmitter extends Base {
12-
fieldDidResetEventEmitter = new EventEmitter();
14+
fieldDidResetEventEmitter = new EventEmitter<[ListenerArg], ListenerReturnType>();
1315

1416
emitFieldDidResetEvent(field: Field) {
1517
return this.fieldDidResetEventEmitter.emitSync(FieldDidResetEvent, field);

packages/react-form-with-constraints/src/withFieldDidValidateEventEmitter.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ export const FieldDidValidateEvent = 'FIELD_DID_VALIDATE_EVENT';
66

77
// See [TypeScript 2.2 Support for Mix-in classes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html)
88
export function withFieldDidValidateEventEmitter<TBase extends Constructor<{}>>(Base: TBase) {
9-
type Listener = (field: Field) => void;
9+
type ListenerArg = Field;
10+
type ListenerReturnType = void;
11+
type Listener = (field: ListenerArg) => ListenerReturnType;
1012

1113
return class FieldDidValidateEventEmitter extends Base {
12-
fieldDidValidateEventEmitter = new EventEmitter();
14+
fieldDidValidateEventEmitter = new EventEmitter<[ListenerArg], ListenerReturnType>();
1315

1416
emitFieldDidValidateEvent(field: Field) {
1517
return this.fieldDidValidateEventEmitter.emitSync(FieldDidValidateEvent, field);

packages/react-form-with-constraints/src/withFieldWillValidateEventEmitter.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ export const FieldWillValidateEvent = 'FIELD_WILL_VALIDATE_EVENT';
55

66
// See [TypeScript 2.2 Support for Mix-in classes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html)
77
export function withFieldWillValidateEventEmitter<TBase extends Constructor<{}>>(Base: TBase) {
8-
type Listener = (fieldName: string) => void;
8+
type ListenerArg = string;
9+
type ListenerReturnType = void;
10+
type Listener = (fieldName: ListenerArg) => ListenerReturnType;
911

1012
return class FieldWillValidateEventEmitter extends Base {
11-
fieldWillValidateEventEmitter = new EventEmitter();
13+
fieldWillValidateEventEmitter = new EventEmitter<[ListenerArg], ListenerReturnType>();
1214

1315
emitFieldWillValidateEvent(fieldName: string) {
1416
return this.fieldWillValidateEventEmitter.emitSync(FieldWillValidateEvent, fieldName);

packages/react-form-with-constraints/src/withValidateFieldEventEmitter.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ export const ValidateFieldEvent = 'VALIDATE_FIELD_EVENT';
88
export function withValidateFieldEventEmitter<ListenerReturnType, TBase extends Constructor<{}>>(
99
Base: TBase
1010
) {
11-
type Listener = (input: InputElement) => ListenerReturnType | Promise<ListenerReturnType>;
11+
type ListenerArg = InputElement;
12+
type Listener = (input: ListenerArg) => ListenerReturnType | Promise<ListenerReturnType>;
1213

1314
return class ValidateFieldEventEmitter extends Base {
14-
validateFieldEventEmitter = new EventEmitter<ListenerReturnType>();
15+
validateFieldEventEmitter = new EventEmitter<[ListenerArg], ListenerReturnType>();
1516

1617
emitValidateFieldEvent(input: InputElement) {
1718
return this.validateFieldEventEmitter.emitAsync(ValidateFieldEvent, input);

0 commit comments

Comments
 (0)