1

I'm using Twilio.js library on my application (not Twilio Node) and there's no module nor typings for this library available. There's only a Twilio global variable available that one can use.

The simplest ambient declaration one can have to avoid errors in the IDE is this:

declare const Twilio: any;

But I want to go a little bit further and for that I've been reading TypeScript handbook and few other resources. I've taken especially attention to this link.

And here's what I have so far:

declare const Twilio: Twilio.Base;

declare namespace Twilio {

    export interface Base {
        Device: Device;
    }

    export interface Device {
        ready(handler: DeviceCallback): void;
    }

    export interface DeviceCallback {
        (device: Device): void;
    }

}

This is working, but it's just a sample, it's not complete. As an example, it suffices for now :)

But my question is three-fold:

  1. Given the short sample above, would you do anything differently?
  2. If I remove the export keyword from all interfaces, it still works. Should I still leave it? What does it do?
  3. Given the usage Twilio.Device.ready(this.handleTwilioDeviceReady.bind(this));, IDEs give me the following when hovering my mouse over:

    • Twilio: const Twilio: Twilio.Base
    • Twilio.Device: (property) Twilio.Base.Device: Twilio.Device
    • Twilio.Device.ready: (method) Twilio.Device.ready(handler: Twilio.DeviceCallback): void

    • How can I get rid of Twilio.Base showing up in the IDE and instead show:

      • Twilio: const Twilio: Twilio
      • Twilio.Device: (property) Twilio.Device: Twilio.Device
rfgamaral
  • 16,546
  • 57
  • 163
  • 275
  • Looks like there is a type definition for Twilio available as `@types/twilio` via npm: https://www.npmjs.com/package/@types/twilio – Seamus Feb 18 '17 at 18:13
  • @Seamus That's for [Twilio Node.js helper library](https://twilio.github.io/twilio-node/), not for the one I'm using. Forgot to link that... – rfgamaral Feb 18 '17 at 20:01
  • 1
    Just fyi, `Twilio.Device.ready(() => this.handleTwilioDeviceReady());` would be more idiomatic TypeScript :) – JKillian Feb 18 '17 at 23:07
  • @JKillian Thanks for the suggestion. I'd prefer something more like [autobin-decorator](https://github.com/andreypopp/autobind-decorator) though, but I'm having an [issue](https://github.com/andreypopp/autobind-decorator/issues/43) getting it to work :( – rfgamaral Feb 19 '17 at 10:44

1 Answers1

1

I've taken a look at the API and I think the below should be a good starting point as an ambient declaration file.

declare namespace Twilio {

    class Connection {
        // Constructor does not appear to be available to user.
        private constructor();
        // Add Connection Methods and Properties Here
    }

    // Not immediately clear if Twilio.Device is a class or not.
    interface IDevice {
        setup(token, options);
        ready(handler);
        offline(handler);
        incoming(handler);
        connect(params) : Connection;
        // Add Remainder of Twilio.Device properties here.
    }

    /**
     * Twilio.Device appears to be a singleton object that 
     * you don't instantiate yourself. You can use
     * the below to declare its presence.
     */
    let Device : IDevice;
}

A few more notes:

declare const Twilio: Twilio.Base;

This is made redundant by the following namespace declaration, which has the effect of declaring the presence of a plain old JS object with the name and members you've declared.

Export doesn't appear to have any function when declaring ambient classes/namespaces. It's only necessary if you're declaring a module with a default export member, or if you're writing a TS file and need to declare which classes and interfaces will be publicly accessible.

EDIT : Callback Type for Device.Ready

IDevice has the ready method, which accepts an function argument that's passed an IDevice object, and isn't expected to return anything. The type signature for such a function is:

(device : IDevice) => void;

An inline declaration would be:

ready((device : IDevice) => void) : void;

Given that you're going to reuse this callback type a few times, you should create a type alias and then refer to it like so:

type DeviceCallback = (device : IDevice) => void;

interface IDevice {
    ...
    ready(handler: DeviceCallback) : void;
    offline(handler : DeviceCallback) : void;
    ...
}
Rich Seviora
  • 1,789
  • 12
  • 16
  • Great, exactly what I was looking for :) I do have 2 follow up questions, one probably requires a new SO question... As an example, given the Twilio documentation for this: `.ready( handler(device) )` how would you properly declare this function? I see two options: **a)** `ready(handler: Function): void` or **b)** `ready(handler: (device: IDevice) => void): void`. What would be your recommendation? Is there a **c)** alternative? – rfgamaral Feb 22 '17 at 11:03
  • My second follow up question needs more text and better examples, do you mind if I create a new question and link it back here for you to take a look? You seem to know your way around TS declaration files and it looks like there isn't too many people with that kind of knowledge around SO. – rfgamaral Feb 22 '17 at 11:05
  • @RicardoAmaral sure thing! Do that and I'll answer both :) – Rich Seviora Feb 22 '17 at 14:48
  • Here it is: http://stackoverflow.com/q/42398170/40480 in the mean time, if you could answer me on the **a)** question above, that would be great. This is just a matter of preference I suppose, so an additional question isn't needed, or do you want one? – rfgamaral Feb 22 '17 at 17:19
  • 1
    @RicardoAmaral I've amended my answer with the question for the handler with the C option :) – Rich Seviora Feb 22 '17 at 18:53
  • Done, thanks very much. I'll award your bounty when allowed :) – rfgamaral Feb 22 '17 at 20:12