aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJack Franklin <[email protected]>2020-07-15 15:31:43 +0100
committerGitHub <[email protected]>2020-07-15 10:31:43 -0400
commit5116df46a020ae498eec6783cfe9147fc9add015 (patch)
tree93be70d93df33d1d60c7c3e4cfed0ae408e272cb
parent244cac2aa3d6831774129d6dc4942465dcef8099 (diff)
Add generic types and update tests (#107)
* Add generic types and update tests * Add generic types to `on`, `off` and `emit` to enable some nicer TS usage if you specify what type you're expecting from the `EventData`. * Move the tests to be TypeScript source. This will help catch errors in the tests if there are any type errors. * Create a new test to test the generic types and make sure they pass and error when expected. * Upgrade to Mocha 8. * Did some tidying up of the package.json scripts. * Tweak TS setup to validate tests * Fix d.ts generation and tests Co-authored-by: Jason Miller <[email protected]>
-rw-r--r--.editorconfig1
-rw-r--r--.gitignore1
-rw-r--r--README.md2
-rw-r--r--package.json35
-rw-r--r--src/index.ts14
-rw-r--r--test/index_test.ts (renamed from test/index.js)6
-rw-r--r--test/test-types-compilation.ts43
-rw-r--r--test/types.ts20
-rw-r--r--tsconfig.json8
9 files changed, 86 insertions, 44 deletions
diff --git a/.editorconfig b/.editorconfig
index ac0adb7..04d7ef9 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -10,6 +10,7 @@ insert_final_newline = true
[{package.json,.*rc,*.yml}]
indent_style = space
indent_size = 2
+insert_final_newline = false
[*.md]
trim_trailing_whitespace = false
diff --git a/.gitignore b/.gitignore
index ecdffab..eb570d3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
/test-reports
/node_modules
/npm-debug.log
+/index.d.ts
package-lock.json
.DS_Store
.idea
diff --git a/README.md b/README.md
index 0796e5f..b6a8022 100644
--- a/README.md
+++ b/README.md
@@ -110,7 +110,7 @@ const emitter: mitt.Emitter = mitt();
Mitt: Tiny (~200b) functional event emitter / pubsub.
-Returns **Mitt**
+Returns **Mitt**
### on
diff --git a/package.json b/package.json
index 90b7b74..a2c2b58 100644
--- a/package.json
+++ b/package.json
@@ -8,12 +8,13 @@
"esmodules": "dist/mitt.modern.js",
"main": "dist/mitt.js",
"umd:main": "dist/mitt.umd.js",
- "typings": "dist/index.d.ts",
+ "typings": "index.d.ts",
"scripts": {
- "test": "npm-run-all --silent typecheck lint testonly",
- "testonly": "mocha --require esm test/**/*.js",
+ "test": "npm-run-all --silent typecheck lint mocha test-types",
+ "mocha": "mocha test",
+ "test-types": "tsc test/test-types-compilation.ts --noEmit",
"lint": "eslint src test --ext ts --ext js",
- "typecheck": "tsc **/*.ts --noEmit",
+ "typecheck": "tsc --noEmit",
"bundle": "microbundle",
"build": "npm-run-all --silent clean -p bundle -s docs",
"clean": "rimraf dist",
@@ -34,8 +35,21 @@
"license": "MIT",
"files": [
"src",
- "dist"
+ "dist",
+ "index.d.ts"
],
+ "mocha": {
+ "extension": [
+ "ts"
+ ],
+ "require": [
+ "ts-node/register",
+ "esm"
+ ],
+ "spec": [
+ "test/*_test.ts"
+ ]
+ },
"eslintConfig": {
"extends": [
"developit",
@@ -68,7 +82,8 @@
}
},
"eslintIgnore": [
- "dist"
+ "dist",
+ "index.d.ts"
],
"devDependencies": {
"@types/chai": "^4.2.11",
@@ -82,13 +97,13 @@
"eslint": "^7.1.0",
"eslint-config-developit": "^1.2.0",
"esm": "^3.2.25",
- "microbundle": "^0.12.0",
- "mocha": "^7.2.0",
+ "microbundle": "^0.12.3",
+ "mocha": "^8.0.1",
"npm-run-all": "^4.1.5",
"rimraf": "^3.0.2",
"sinon": "^9.0.2",
"sinon-chai": "^3.5.0",
- "ts-node": "^8.10.1",
+ "ts-node": "^8.10.2",
"typescript": "^3.9.3"
}
-}
+} \ No newline at end of file
diff --git a/src/index.ts b/src/index.ts
index e640292..c681fd0 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -2,8 +2,8 @@ export type EventType = string | symbol;
// An event handler can take an optional event argument
// and should not return a value
-export type Handler = (event?: any) => void;
-export type WildcardHandler = (type: EventType, event?: any) => void
+export type Handler<T = any> = (event?: T) => void;
+export type WildcardHandler = (type: EventType, event?: any) => void;
// An array of all currently registered event handlers for a type
export type EventHandlerList = Array<Handler>;
@@ -13,10 +13,10 @@ export type WildCardEventHandlerList = Array<WildcardHandler>;
export type EventHandlerMap = Map<EventType, EventHandlerList | WildCardEventHandlerList>;
export interface Emitter {
- on(type: EventType, handler: Handler): void;
+ on<T = any>(type: EventType, handler: Handler<T>): void;
on(type: '*', handler: WildcardHandler): void;
- off(type: EventType, handler: Handler): void;
+ off<T = any>(type: EventType, handler: Handler<T>): void;
off(type: '*', handler: WildcardHandler): void;
emit<T = any>(type: EventType, event?: T): void;
@@ -38,7 +38,7 @@ export default function mitt(all?: EventHandlerMap): Emitter {
* @param {Function} handler Function to call in response to given event
* @memberOf mitt
*/
- on(type: EventType, handler: Handler) {
+ on<T = any>(type: EventType, handler: Handler<T>) {
const handlers = all.get(type);
const added = handlers && handlers.push(handler);
if (!added) {
@@ -53,7 +53,7 @@ export default function mitt(all?: EventHandlerMap): Emitter {
* @param {Function} handler Handler function to remove
* @memberOf mitt
*/
- off(type: EventType, handler: Handler) {
+ off<T = any>(type: EventType, handler: Handler<T>) {
const handlers = all.get(type);
if (handlers) {
handlers.splice(handlers.indexOf(handler) >>> 0, 1);
@@ -70,7 +70,7 @@ export default function mitt(all?: EventHandlerMap): Emitter {
* @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler
* @memberOf mitt
*/
- emit(type: EventType, evt: any) {
+ emit<T = any>(type: EventType, evt: T) {
((all.get(type) || []) as EventHandlerList).slice().map((handler) => { handler(evt); });
((all.get('*') || []) as WildCardEventHandlerList).slice().map((handler) => { handler(type, evt); });
}
diff --git a/test/index.js b/test/index_test.ts
index a837d36..1f1d9bb 100644
--- a/test/index.js
+++ b/test/index_test.ts
@@ -1,4 +1,4 @@
-import mitt from '..';
+import mitt, { Emitter } from '..';
import chai, { expect } from 'chai';
import { spy } from 'sinon';
import sinonChai from 'sinon-chai';
@@ -23,7 +23,7 @@ describe('mitt', () => {
});
describe('mitt#', () => {
- let events, inst;
+ let events, inst: Emitter;
beforeEach( () => {
events = new Map();
@@ -143,7 +143,7 @@ describe('mitt#', () => {
it('should invoke handler for type', () => {
const event = { a: 'b' };
- inst.on('foo', (one, two) => {
+ inst.on('foo', (one, two?) => {
expect(one).to.deep.equal(event);
expect(two).to.be.an('undefined');
});
diff --git a/test/test-types-compilation.ts b/test/test-types-compilation.ts
new file mode 100644
index 0000000..00510da
--- /dev/null
+++ b/test/test-types-compilation.ts
@@ -0,0 +1,43 @@
+/* eslint-disable @typescript-eslint/ban-ts-comment, @typescript-eslint/no-unused-vars */
+
+import mitt from '..';
+
+const emitter = mitt();
+
+/*
+ * Check that if on is provided a generic, it only accepts handlers of that type
+ */
+{
+ const badHandler = (x: number) => {};
+ const goodHandler = (x: string) => {};
+
+ // @ts-expect-error
+ emitter.on<string>('foo', badHandler);
+ emitter.on<string>('foo', goodHandler);
+}
+
+/*
+ * Check that if off is provided a generic, it only accepts handlers of that type
+ */
+{
+ const badHandler = (x: number) => {};
+ const goodHandler = (x: string) => {};
+
+ // @ts-expect-error
+ emitter.off<string>('foo', badHandler);
+ emitter.off<string>('foo', goodHandler);
+}
+
+
+/*
+ * Check that if emitt is provided a generic, it only accepts event data of that type
+ */
+{
+ interface SomeEventData {
+ name: string;
+ }
+ // @ts-expect-error
+ emitter.emit<SomeEventData>('foo', 'NOT VALID');
+ emitter.emit<SomeEventData>('foo', { name: 'jack' });
+}
+
diff --git a/test/types.ts b/test/types.ts
deleted file mode 100644
index 23334bb..0000000
--- a/test/types.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import mitt, { EventHandlerList, EventHandlerMap } from '..';
-
-const events = mitt();
-function foo() {}
-events.on('foo', foo);
-events.emit('foo', 'hello');
-
-// handler return type should be ignored:
-events.on('foo', async e => e * 42);
-
-// event map type
-const map = new Map<string, EventHandlerList>([
- ['foo', [foo]]
-]);
-const events2 = mitt(map);
-events2.emit('foo', 'hello');
-
-// event map type & iterables
-const map2 : EventHandlerMap = new Map(Object.entries(({ foo: [foo] })));
-mitt(map2);
diff --git a/tsconfig.json b/tsconfig.json
index 2610831..acab4f5 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -3,9 +3,11 @@
"compilerOptions": {
"noEmit": true,
"declaration": true,
- "moduleResolution": "node"
+ "moduleResolution": "node",
+ "esModuleInterop": true
},
- "exclude": [
- "test"
+ "include": [
+ "src/*.ts",
+ "test/*.ts",
]
}