Skip to content

Commit 53eb689

Browse files
authored
Merge branch 'master' into patch_123
2 parents ec9122b + f0f36b0 commit 53eb689

File tree

8 files changed

+169
-69
lines changed

8 files changed

+169
-69
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66
package-lock.json
77
.DS_Store
88
.idea
9+
.vscode

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2021 Jason Miller
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,17 @@ emitter.off('foo', onFoo) // unlisten
8080

8181
### Typescript
8282

83+
Set `"strict": true` in your tsconfig.json to get improved type inference for `mitt` instance methods.
84+
8385
```ts
8486
import mitt from 'mitt';
85-
const emitter: mitt.Emitter = mitt();
87+
88+
type Events = {
89+
foo: string
90+
bar?: number
91+
}
92+
93+
const emitter: mitt.Emitter<Events> = mitt<Events>();
8694
```
8795

8896
## Examples & Demos
@@ -126,7 +134,7 @@ Register an event handler for the given type.
126134

127135
#### Parameters
128136

129-
- `type` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [symbol](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Symbol))** Type of event to listen for, or `"*"` for all events
137+
- `type` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [symbol](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Symbol))** Type of event to listen for, or `'*'` for all events
130138
- `handler` **[Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)** Function to call in response to given event
131139

132140
### off
@@ -136,15 +144,15 @@ If omit the `handler`, all event handlers of the given type are deleted.
136144

137145
#### Parameters
138146

139-
- `type` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [symbol](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Symbol))** Type of event to unregister `handler` from, or `"*"`
147+
- `type` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [symbol](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Symbol))** Type of event to unregister `handler` from, or `'*'`
140148
- `handler` **[Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)** Handler function to remove
141149

142150
### emit
143151

144152
Invoke all handlers for the given type.
145-
If present, `"*"` handlers are invoked after type-matched handlers.
153+
If present, `'*'` handlers are invoked after type-matched handlers.
146154

147-
Note: Manually firing "\*" handlers is not supported.
155+
Note: Manually firing '\*' handlers is not supported.
148156

149157
#### Parameters
150158

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"scripts": {
1313
"test": "npm-run-all --silent typecheck lint mocha test-types",
1414
"mocha": "mocha test",
15-
"test-types": "tsc test/test-types-compilation.ts --noEmit",
15+
"test-types": "tsc test/test-types-compilation.ts --noEmit --strict",
1616
"lint": "eslint src test --ext ts --ext js",
1717
"typecheck": "tsc --noEmit",
1818
"bundle": "microbundle",
@@ -34,7 +34,6 @@
3434
],
3535
"license": "MIT",
3636
"files": [
37-
"src",
3837
"dist",
3938
"index.d.ts"
4039
],
@@ -78,7 +77,8 @@
7877
"@typescript-eslint/no-explicit-any": 0,
7978
"@typescript-eslint/explicit-function-return-type": 0,
8079
"@typescript-eslint/explicit-module-boundary-types": 0,
81-
"@typescript-eslint/no-empty-function": 0
80+
"@typescript-eslint/no-empty-function": 0,
81+
"@typescript-eslint/no-non-null-assertion": 0
8282
}
8383
},
8484
"eslintIgnore": [
@@ -104,6 +104,6 @@
104104
"sinon": "^9.0.2",
105105
"sinon-chai": "^3.5.0",
106106
"ts-node": "^8.10.2",
107-
"typescript": "^3.9.3"
107+
"typescript": "^3.9.7"
108108
}
109-
}
109+
}

src/index.ts

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,46 @@ export type EventType = string | symbol;
22

33
// An event handler can take an optional event argument
44
// and should not return a value
5-
export type Handler<T = any> = (event?: T) => void;
6-
export type WildcardHandler = (type: EventType, event?: any) => void;
5+
export type Handler<T = unknown> = (event: T) => void;
6+
export type WildcardHandler<T = Record<string, unknown>> = (
7+
type: keyof T,
8+
event: T[keyof T]
9+
) => void;
710

811
// An array of all currently registered event handlers for a type
9-
export type EventHandlerList = Array<Handler>;
10-
export type WildCardEventHandlerList = Array<WildcardHandler>;
12+
export type EventHandlerList<T = unknown> = Array<Handler<T>>;
13+
export type WildCardEventHandlerList<T = Record<string, unknown>> = Array<WildcardHandler<T>>;
1114

1215
// A map of event types and their corresponding event handlers.
13-
export type EventHandlerMap = Map<EventType, EventHandlerList | WildCardEventHandlerList>;
16+
export type EventHandlerMap<Events extends Record<EventType, unknown>> = Map<
17+
keyof Events | '*',
18+
EventHandlerList<Events[keyof Events]> | WildCardEventHandlerList<Events>
19+
>;
1420

15-
export interface Emitter {
16-
all: EventHandlerMap;
21+
export interface Emitter<Events extends Record<EventType, unknown>> {
22+
all: EventHandlerMap<Events>;
1723

18-
on<T = any>(type: EventType, handler: Handler<T>): void;
19-
on(type: '*', handler: WildcardHandler): void;
24+
on<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>): void;
25+
on(type: '*', handler: WildcardHandler<Events>): void;
2026

21-
off<T = any>(type: EventType, handler?: Handler<T>): void;
22-
off(type: '*', handler: WildcardHandler): void;
27+
off<Key extends keyof Events>(type: Key, handler?: Handler<Events[Key]>): void;
28+
off(type: '*', handler: WildcardHandler<Events>): void;
2329

24-
emit<T = any>(type: EventType, event?: T): void;
25-
emit(type: '*', event?: any): void;
30+
emit<Key extends keyof Events>(type: Key, event: Events[Key]): void;
31+
emit<Key extends keyof Events>(type: undefined extends Events[Key] ? Key : never): void;
2632
}
2733

2834
/**
2935
* Mitt: Tiny (~200b) functional event emitter / pubsub.
3036
* @name mitt
3137
* @returns {Mitt}
3238
*/
33-
export default function mitt(all?: EventHandlerMap): Emitter {
39+
export default function mitt<Events extends Record<EventType, unknown>>(
40+
all?: EventHandlerMap<Events>
41+
): Emitter<Events> {
42+
type GenericEventHandler =
43+
| Handler<Events[keyof Events]>
44+
| WildcardHandler<Events>;
3445
all = all || new Map();
3546

3647
return {
@@ -42,27 +53,27 @@ export default function mitt(all?: EventHandlerMap): Emitter {
4253

4354
/**
4455
* Register an event handler for the given type.
45-
* @param {string|symbol} type Type of event to listen for, or `"*"` for all events
56+
* @param {string|symbol} type Type of event to listen for, or `'*'` for all events
4657
* @param {Function} handler Function to call in response to given event
4758
* @memberOf mitt
4859
*/
49-
on<T = any>(type: EventType, handler: Handler<T>) {
50-
const handlers = all.get(type);
60+
on<Key extends keyof Events>(type: Key, handler: GenericEventHandler) {
61+
const handlers: Array<GenericEventHandler> | undefined = all!.get(type);
5162
const added = handlers && handlers.push(handler);
5263
if (!added) {
53-
all.set(type, [handler]);
64+
all!.set(type, [handler] as EventHandlerList<Events[keyof Events]>);
5465
}
5566
},
5667

5768
/**
5869
* Remove an event handler for the given type.
59-
* If omit the `handler`, all event handlers of the given type are deleted.
60-
* @param {string|symbol} type Type of event to unregister `handler` from, or `"*"`
61-
* @param {Function} handler Handler function to remove
70+
* If `handler` is omitted, all handlers of the given type are removed.
71+
* @param {string|symbol} type Type of event to unregister `handler` from, or `'*'`
72+
* @param {Function} [handler] Handler function to remove
6273
* @memberOf mitt
6374
*/
64-
off<T = any>(type: EventType, handler?: Handler<T>) {
65-
const handlers = all.get(type);
75+
off<Key extends keyof Events>(type: Key, handler?: GenericEventHandler) {
76+
const handlers: Array<GenericEventHandler> | undefined = all!.get(type);
6677
if (handlers) {
6778
if (handler) {
6879
handlers.splice(handlers.indexOf(handler) >>> 0, 1);
@@ -75,17 +86,32 @@ export default function mitt(all?: EventHandlerMap): Emitter {
7586

7687
/**
7788
* Invoke all handlers for the given type.
78-
* If present, `"*"` handlers are invoked after type-matched handlers.
89+
* If present, `'*'` handlers are invoked after type-matched handlers.
7990
*
80-
* Note: Manually firing "*" handlers is not supported.
91+
* Note: Manually firing '*' handlers is not supported.
8192
*
8293
* @param {string|symbol} type The event type to invoke
8394
* @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler
8495
* @memberOf mitt
8596
*/
86-
emit<T = any>(type: EventType, evt: T) {
87-
((all.get(type) || []) as EventHandlerList).slice().map((handler) => { handler(evt); });
88-
((all.get('*') || []) as WildCardEventHandlerList).slice().map((handler) => { handler(type, evt); });
97+
emit<Key extends keyof Events>(type: Key, evt?: Events[Key]) {
98+
let handlers = all!.get(type);
99+
if (handlers) {
100+
(handlers as EventHandlerList<Events[keyof Events]>)
101+
.slice()
102+
.map((handler) => {
103+
handler(evt!);
104+
});
105+
}
106+
107+
handlers = all!.get('*');
108+
if (handlers) {
109+
(handlers as WildCardEventHandlerList<Events>)
110+
.slice()
111+
.map((handler) => {
112+
handler(type, evt!);
113+
});
114+
}
89115
}
90116
};
91117
}

test/index_test.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import mitt, { Emitter } from '..';
1+
import mitt, { Emitter, EventHandlerMap } from '..';
22
import chai, { expect } from 'chai';
33
import { spy } from 'sinon';
44
import sinonChai from 'sinon-chai';
@@ -15,17 +15,29 @@ describe('mitt', () => {
1515
const a = spy();
1616
const b = spy();
1717
map.set('foo', [a, b]);
18-
const events = mitt(map);
18+
const events = mitt<{ foo: undefined }>(map);
1919
events.emit('foo');
2020
expect(a).to.have.been.calledOnce;
2121
expect(b).to.have.been.calledOnce;
2222
});
2323
});
2424

2525
describe('mitt#', () => {
26-
let events, inst: Emitter;
27-
28-
beforeEach( () => {
26+
const eventType = Symbol('eventType');
27+
type Events = {
28+
foo: unknown;
29+
constructor: unknown;
30+
FOO: unknown;
31+
bar: unknown;
32+
Bar: unknown;
33+
'baz:bat!': unknown;
34+
'baz:baT!': unknown;
35+
Foo: unknown;
36+
[eventType]: unknown;
37+
};
38+
let events: EventHandlerMap<Events>, inst: Emitter<Events>;
39+
40+
beforeEach(() => {
2941
events = new Map();
3042
inst = mitt(events);
3143
});
@@ -83,7 +95,6 @@ describe('mitt#', () => {
8395

8496
it('can take symbols for event types', () => {
8597
const foo = () => {};
86-
const eventType = Symbol('eventType');
8798
inst.on(eventType, foo);
8899
expect(events.get(eventType)).to.deep.equal([foo]);
89100
});
@@ -151,7 +162,7 @@ describe('mitt#', () => {
151162
it('should invoke handler for type', () => {
152163
const event = { a: 'b' };
153164

154-
inst.on('foo', (one, two?) => {
165+
inst.on('foo', (one, two?: unknown) => {
155166
expect(one).to.deep.equal(event);
156167
expect(two).to.be.an('undefined');
157168
});

0 commit comments

Comments
 (0)