TypeScript 是否支持类上的事件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12881212/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Does TypeScript support events on classes?
提问by CompareTheMooCat
I am just wondering if in TypeScript you can define custom events on your classes or interfaces?
我只是想知道在 TypeScript 中是否可以在类或接口上定义自定义事件?
What would this look like?
这会是什么样子?
回答by Jason Kleban
How about this simplified event to be used as a property? Stronger typing of the owning class and no inheritance requirement:
这个简化的事件如何用作属性?拥有类的更强类型并且没有继承要求:
interface ILiteEvent<T> {
on(handler: { (data?: T): void }) : void;
off(handler: { (data?: T): void }) : void;
}
class LiteEvent<T> implements ILiteEvent<T> {
private handlers: { (data?: T): void; }[] = [];
public on(handler: { (data?: T): void }) : void {
this.handlers.push(handler);
}
public off(handler: { (data?: T): void }) : void {
this.handlers = this.handlers.filter(h => h !== handler);
}
public trigger(data?: T) {
this.handlers.slice(0).forEach(h => h(data));
}
public expose() : ILiteEvent<T> {
return this;
}
}
used like so:
像这样使用:
class Security{
private readonly onLogin = new LiteEvent<string>();
private readonly onLogout = new LiteEvent<void>();
public get LoggedIn() { return this.onLogin.expose(); }
public get LoggedOut() { return this.onLogout.expose(); }
// ... onLogin.trigger('bob');
}
function Init() {
var security = new Security();
var loggedOut = () => { /* ... */ }
security.LoggedIn.on((username?) => { /* ... */ });
security.LoggedOut.on(loggedOut);
// ...
security.LoggedOut.off(loggedOut);
}
Improvements?
改进?
回答by Kees C. Bakker
The NPM package Strongly Typed Events for TypeScript(GitHub) implements 3 types of events: IEvent<TSender, TArgs>
, ISimpleEvent<TArgs>
and ISignal
. This makes it easier to use the right kind of event for your project. It also hides the dispatch method from your event, as good information hiding should do.
用于 TypeScript的 NPM 包强类型事件( GitHub) 实现了 3 种类型的事件:IEvent<TSender, TArgs>
,ISimpleEvent<TArgs>
和ISignal
. 这样可以更轻松地为您的项目使用正确类型的事件。它还对您的事件隐藏了 dispatch 方法,就像良好的信息隐藏应该做的那样。
Event Types / Interfaces- The definitions of the events:
事件类型/接口-事件的定义:
interface IEventHandler<TSender, TArgs> {
(sender: TSender, args: TArgs): void
}
interface ISimpleEventHandler<TArgs> {
(args: TArgs): void
}
interface ISignalHandler {
(): void;
}
Example- This example shows how the 3 types of events can be implemented using a ticking clock:
示例- 此示例显示了如何使用滴答时钟实现 3 种类型的事件:
class Clock {
//implement events as private dispatchers:
private _onTick = new SignalDispatcher();
private _onSequenceTick = new SimpleEventDispatcher<number>();
private _onClockTick = new EventDispatcher<Clock, number>();
private _ticks: number = 0;
constructor(public name: string, timeout: number) {
window.setInterval( () => {
this.Tick();
}, timeout);
}
private Tick(): void {
this._ticks += 1;
//trigger event by calling the dispatch method and provide data
this._onTick.dispatch();
this._onSequenceTick.dispatch(this._ticks);
this._onClockTick.dispatch(this, this._ticks);
}
//expose the events through the interfaces - use the asEvent
//method to prevent exposure of the dispatch method:
public get onTick(): ISignal {
return this._onTick.asEvent();
}
public get onSequenceTick() : ISimpleEvent<number>{
return this._onSequenceTick.asEvent();
}
public get onClockTick(): IEvent<Clock, number> {
return this._onClockTick.asEvent();
}
}
Usage- It can be used like this:
用法- 可以这样使用:
let clock = new Clock('Smu', 1000);
//log the ticks to the console
clock.onTick.subscribe(()=> console.log('Tick!'));
//log the sequence parameter to the console
clock.onSequenceTick.subscribe((s) => console.log(`Sequence: ${s}`));
//log the name of the clock and the tick argument to the console
clock.onClockTick.subscribe((c, n) => console.log(`${c.name} ticked ${n} times.`))
Read more here: On events, dispatchers and lists (a general explanation of the system)
在此处阅读更多信息:关于事件、调度程序和列表(系统的一般说明)
Tutorials
I've written a few tutorials on the subject:
教程
我写了一些关于这个主题的教程:
回答by Ezward
I think you are asking if a class instance can implement addEventListener() and dispatchEvent() like a DOM element. If the class is not a DOM node, then you would have to write your own event bus. You would define an interface for a class that can publish events, then implement the interface in the your classes. Here is a naive example;
我想您是在问类实例是否可以像 DOM 元素一样实现 addEventListener() 和 dispatchEvent() 。如果该类不是 DOM 节点,则您必须编写自己的事件总线。您将为可以发布事件的类定义一个接口,然后在您的类中实现该接口。这是一个天真的例子;
interface IEventDispatcher{
// maintain a list of listeners
addEventListener(theEvent:string, theHandler:any);
// remove a listener
removeEventListener(theEvent:string, theHandler:any);
// remove all listeners
removeAllListeners(theEvent:string);
// dispatch event to all listeners
dispatchAll(theEvent:string);
// send event to a handler
dispatchEvent(theEvent:string, theHandler:any);
}
class EventDispatcher implement IEventDispatcher {
private _eventHandlers = {};
// maintain a list of listeners
public addEventListener(theEvent:string, theHandler:any) {
this._eventHandlers[theEvent] = this._eventHandlers[theEvent] || [];
this._eventHandlers[theEvent].push(theHandler);
}
// remove a listener
removeEventListener(theEvent:string, theHandler:any) {
// TODO
}
// remove all listeners
removeAllListeners(theEvent:string) {
// TODO
}
// dispatch event to all listeners
dispatchAll(theEvent:string) {
var theHandlers = this._eventHandlers[theEvent];
if(theHandlers) {
for(var i = 0; i < theHandlers.length; i += 1) {
dispatchEvent(theEvent, theHandlers[i]);
}
}
}
// send event to a handler
dispatchEvent(theEvent:string, theHandler:any) {
theHandler(theEvent);
}
}
回答by Fenton
You can use custom events in TypeScript. I'm not sure exactly what you are trying to do, but here is an example:
您可以在 TypeScript 中使用自定义事件。我不确定你到底想做什么,但这里有一个例子:
module Example {
export class ClassWithEvents {
public div: HTMLElement;
constructor (id: string) {
this.div = document.getElementById(id);
// Create the event
var evt = document.createEvent('Event');
evt.initEvent('customevent', true, true);
// Create a listener for the event
var listener = function (e: Event) {
var element = <HTMLElement> e.target;
element.innerHTML = 'hello';
}
// Attach the listener to the event
this.div.addEventListener('customevent', listener);
// Trigger the event
this.div.dispatchEvent(evt);
}
}
}
If you are looking to do something more specific please let me know.
如果您想做更具体的事情,请告诉我。
回答by Jason Young
If you are looking to get intelli-sense type checking using the standard emitter pattern you can now do the following:
如果您希望使用标准发射器模式进行智能感知类型检查,您现在可以执行以下操作:
type DataEventType = "data";
type ErrorEventType = "error";
declare interface IDataStore<TResponse> extends Emitter {
on(name: DataEventType, handler : (data: TResponse) => void);
on(name: ErrorEventType, handler: (error: any) => void);
}
回答by Jailen
回答by vitaly-t
Here's a simple example of adding custom-type events to your class, using sub-events:
这是使用子事件向类添加自定义类型事件的简单示例:
class MyClass {
readonly onMessage: SubEvent<string> = new SubEvent();
readonly onData: SubEvent<MyCustomType> = new SubEvent();
sendMessage(msg: string) {
this.onMessage.emit(msg);
}
sendData(data: MyCustomType) {
this.onData.emit(data);
}
}
And then any client can subscribe to receive those events:
然后任何客户端都可以订阅接收这些事件:
const a = new MyClass();
const sub1 = a.onMessage.subscribe(msg => {
// msg here is strongly-typed
});
const sub2 = a.onData.subscribe(data => {
// data here is strongly-typed
});
And when you no longer need the events, you can cancel the subscriptions:
当您不再需要这些事件时,您可以取消订阅:
sub1.cancel();
sub2.cancel();
回答by user2866593
This solution allows you to directly write the parameters in the function call instead of needing to wrap all your parameters in an object.
该解决方案允许您直接在函数调用中写入参数,而无需将所有参数包装在一个对象中。
interface ISubscription {
(...args: any[]): void;
}
class PubSub<T extends ISubscription> {
protected _subscribed : ISubscriptionItem[] = [];
protected findSubscription(event : T) : ISubscriptionItem {
this._subscribed.forEach( (item : ISubscriptionItem) =>{
if (item.func==event)
return item;
} );
return null;
}
public sub(applyObject : any,event : T) {
var newItem = this.findSubscription(event);
if (!newItem) {
newItem = {object : applyObject, func : event };
this._subscribed.push(newItem);
this.doChangedEvent();
}
}
public unsub(event : T) {
for ( var i=this._subscribed.length-1 ; i>=0; i--) {
if (this._subscribed[i].func==event)
this._subscribed.splice(i,1);
}
this.doChangedEvent();
}
protected doPub(...args: any[]) {
this._subscribed.forEach((item : ISubscriptionItem)=> {
item.func.apply(item.object, args);
})
}
public get pub() : T {
var pubsub=this;
var func= (...args: any[]) => {
pubsub.doPub(args);
}
return <T>func;
}
public get pubAsync() : T {
var pubsub=this;
var func = (...args: any[]) => {
setTimeout( () => {
pubsub.doPub(args);
});
}
return <T>func;
}
public get count() : number {
return this._subscribed.length
}
}
Usage:
用法:
interface ITestEvent {
(test : string): void;
}
var onTestEvent = new PubSub<ITestEvent>();
//subscribe to the event
onTestEvent.sub(monitor,(test : string) => {alert("called:"+test)});
//call the event
onTestEvent.pub("test1");