From 342615bd7faaa27c3817bd46321cc457eff33bac Mon Sep 17 00:00:00 2001 From: Julian Gonggrijp Date: Fri, 15 Feb 2019 02:10:19 +0100 Subject: [PATCH] Add shorthand interfaces for the Backbone.Events methods --- types/backbone/backbone-tests.ts | 44 +++++++++++++++++++++ types/backbone/index.d.ts | 67 ++++++++++++++++++++++++++++---- 2 files changed, 104 insertions(+), 7 deletions(-) diff --git a/types/backbone/backbone-tests.ts b/types/backbone/backbone-tests.ts index 96d447c1b3..c36b92ff3f 100644 --- a/types/backbone/backbone-tests.ts +++ b/types/backbone/backbone-tests.ts @@ -15,6 +15,50 @@ function test_events() { object.off(); } +class PubSub implements Backbone.Events { + on: Backbone.Events_On; + off: Backbone.Events_Off; + trigger: Backbone.Events_Trigger; + bind: Backbone.Events_On; + unbind: Backbone.Events_Off; + + once: Backbone.Events_On; + listenTo: Backbone.Events_Listen; + listenToOnce: Backbone.Events_Listen; + stopListening: Backbone.Events_Stop; +} + +Object.assign(PubSub.prototype, Backbone.Events); + +function test_events_shorthands() { + let channel1 = new PubSub(); + let channel2 = new PubSub(); + let onChange = () => alert('whatever'); + + channel1.on("alert", (eventName: string) => alert("Triggered " + eventName)); + channel1.trigger("alert", "an event"); + + channel1.once('invalid', () => { }, this); + channel1.once('invalid', () => { }); + channel1.once({invalid: () => { }, success: () => { }}, this); + channel1.once({invalid: () => { }, success: () => { }}); + + channel1.off("change", onChange); + channel1.off("change"); + channel1.off(null, onChange); + channel1.off(null, null, this); + channel1.off(); + + channel2.listenTo(channel1, 'invalid', () => { }); + channel2.listenTo(channel1, {invalid: () => { }, success: () => { }}); + channel2.listenToOnce(channel1, 'invalid', () => { }); + channel2.listenToOnce(channel1, {invalid: () => { }, success: () => { }}); + + channel2.stopListening(channel1, 'invalid', () => { }); + channel2.stopListening(channel1, 'invalid'); + channel2.stopListening(channel1); +} + class SettingDefaults extends Backbone.Model { // 'defaults' could be set in one of the following ways: diff --git a/types/backbone/index.d.ts b/types/backbone/index.d.ts index dcf4d44c84..1fa83dbead 100644 --- a/types/backbone/index.d.ts +++ b/types/backbone/index.d.ts @@ -110,13 +110,14 @@ declare namespace Backbone { } /** - * Helper to avoid code repetition. Backbone.Events cannot be extended, - * hence a separate abstract class with a different name. - * Both classes and interfaces can extend from this helper class to - * reuse the signatures, but only in type declarations. - * Classes that already extend another base class can still - * `implements Events`, but in this case, unfortunately you have to - * repeat all signatures below. + * Helper to avoid code repetition in type declarations. + * Backbone.Events cannot be extended, hence a separate abstract + * class with a different name. Both classes and interfaces can + * extend from this helper class to reuse the signatures. + * + * For class type declarations that already extend another base + * class, and for actual class definitions, please see the + * EventsMethod* interfaces below. */ abstract class EventsMixin implements Events { on(eventName: string, callback: EventHandler, context?: any): this; @@ -139,6 +140,58 @@ declare namespace Backbone { export const Events: Events; interface Events extends EventsMixin { } + /** + * Helper shorthands for classes that implement the Events interface. + * Define your class like this: + * + * import { + * Events, + * Events_On, + * Events_Off, + * Events_Trigger, + * Events_Listen, + * Events_Stop, + * } from 'backbone'; + * + * class YourClass implements Events { + * on: Events_On; + * off: Events_Off; + * trigger: Events_Trigger; + * bind: Events_On; + * unbind: Events_Off; + * + * once: Events_On; + * listenTo: Events_Listen; + * listenToOnce: Events_Listen; + * stopListening: Events_Stop; + * + * // ... (other methods) + * } + * + * Object.assign(YourClass.prototype, Events); // can also use _.extend + * + * If you are just writing a class type declaration that doesn't already + * extend some other base class, you can use the EventsMixin instead; + * see above. + */ + interface Events_On { + (this: T, eventName: string, callback: EventHandler, context?: any): T; + (this: T, eventMap: EventMap, context?: any): T; + } + interface Events_Off { + (this: T, eventName?: string, callback?: EventHandler, context?: any): T; + } + interface Events_Trigger { + (this: T, eventName: string, ...args: any[]): T; + } + interface Events_Listen { + (this: T, object: any, events: string, callback: EventHandler): T; + (this: T, object: any, eventMap: EventMap): T; + } + interface Events_Stop { + (this: T, object?: any, events?: string, callback?: EventHandler): T; + } + class ModelBase extends EventsMixin { parse(response: any, options?: any): any; toJSON(options?: any): any;