Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeScript Bindings #72

Merged
merged 7 commits into from
Sep 30, 2016
Merged

TypeScript Bindings #72

merged 7 commits into from
Sep 30, 2016

Conversation

delta62
Copy link
Contributor

@delta62 delta62 commented Sep 29, 2016

This pull request adds TypeScript bindings for the public interface of eventemitter3 (See issue #67).

The typings were written in the UMD style - that is, they will work correctly for scripts using module loaders as well as for scripts that reference EventEmitter as a global.

* Return the listeners registered for a given event.
*/
listeners(event: string | symbol): Array<ListenerFn>;
listeners(event: string | symbol, exists: boolean): Array<ListenerFn> | boolean;
Copy link
Member

@lpinca lpinca Sep 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should exists be exists?? It is optional.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In TypeScript definitions, you can provide overloaded typings for the same method.

One implementation only takes event and always returns an array of listeners. The overload takes both parameters and either returns an array of listeners or a boolean, depending on the value of exists.

It is a best practice to put the most specific overload first, so I did swap the order of these in 365bef6.

/**
* Remove all listeners, or those of the specified event.
*/
removeAllListeners(event: string | symbol): EventEmitter;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

event is optional.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 a711d93

type ListenerFn = (...args: Array<any>) => void;

/**
* Minimal `EventEmitter` interface that is molded against the Node.js
Copy link
Member

@lpinca lpinca Sep 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please "fix" the indentation of these comments? Indent the second line and the following with 1 space.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoa.. good catch. That's weird. 🔧 3e5f97a

@lpinca
Copy link
Member

lpinca commented Sep 29, 2016

Looks good but my experience with TypeScript is close to zero so if another pair of eyes could review this it would be great.

@lpinca
Copy link
Member

lpinca commented Sep 29, 2016

cc: @Stubb0rn @roblav96

@Stubb0rn
Copy link

Stubb0rn commented Sep 29, 2016

I believe module exports are not defined correctly. This is single export CommonJS module that exports non-module entity (class). This type of export cannot be defined using ES6 export syntax. You can take a look at discussion for PR that I created for this library in DefinitelyTyped repository DefinitelyTyped/DefinitelyTyped#10857

UPD. Nevermind, didn't notice related changes in #71

@lpinca
Copy link
Member

lpinca commented Sep 29, 2016

@Stubb0rn so LGTY?

* `EventEmitter` interface.
*/
export class EventEmitter {
prefix: string;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This property should be static

Copy link
Member

@lpinca lpinca Sep 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, also the correct name is prefixed and can be either a string or a boolean (false).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 9c00931

/**
* Add a listener for a given event.
*/
on(event: string | symbol, fn: ListenerFn, context?: any): EventEmitter;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of EventEmitter it is better to use this as return type of methods that support fluent interface. This will enable access to own methods of classes that extend EventEmitter:

class TestEmitter extends EventEmitter {
  ...
  test() {}
}

new TestEmitter().on().test();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 780c427

@@ -0,0 +1,50 @@
export as namespace EventEmitter;
Copy link

@Stubb0rn Stubb0rn Sep 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current definition of exports might work well with ES6 syntax but not so well with standard TypeScript import syntax:

import EventEmitter = require('eventemitter3');
new EventEmitter();

This code will not compile with current export definition and this syntax could be used by many users of this library that use it with TypeScript (especially in Node environment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's true that we cannot support both the import = and the ES6-style imports with the type definitions. We need to choose one. I put a bit of consideration into this when I wrote the defs.

The problem with import = is that if you use it anywhere in your code, you cannot compile into ES6 modules. I need to enable that setting, which was my motivation for this PR as well as #71.

Any users who are currently using this repository with TypeScript either wrote their own definitions or are using the (old) ones from DefinitelyTyped. I wonder what happens if this repository had these definitions included and the user had the DT definitions installed as well. That may allow legacy code to continue working while new projects can use the ES6 import syntax.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that shipping typings that use ES6-style imports will break anyone's existing code.

From TS design discussion:

If exists ambient external module named X {
  return the ambient external module 
}
else if X begins with './' or '../' or it is rooted path {
  try LOAD_AS_FILE(Y + X, loadOnlyDts=false)
  try LOAD_AS_DIRECTORY(Y + X, loadOnlyDts=false)
}
else {
  LOAD_NODE_MODULES(X, dirname(Y))
}
THROW "not found"

So if a user already has .d.ts files installed for eventemitter3, the definitions in this node module would not be used. This is consistent with what I tested locally.

@roblav96
Copy link

all looks good to me :D

@lpinca lpinca merged commit 165c59c into primus:master Sep 30, 2016
@lpinca
Copy link
Member

lpinca commented Sep 30, 2016

@delta62 @Stubb0rn @roblav96 thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants