Using jQuery plugin in TypeScript

When using typescript do I need to import a plugin.d.ts for every external js that I use? In other words, do I need to create a jQuery.d.ts with all the interfaces?


Solution 1:

The issue with jQuery plugins (and other plugin based libraries) is that not only do you need a library.d.ts file for the base library, but you also need a plugin.d.ts file for each plugin. And somehow thes plugin.d.ts files need to extend the library interfaces defined in the library.d.ts files. Fortunately, TypeScript has a nifty little feature that lets you do just that.

With classes there currently can only be a single cononical definition of a class within a project. So if you define a class Foo the members you put on Foo are all you get. Any additional definitions of Foo will result in an error. With interfaces, however, the members are additive so if you define interface Bar with a set of members you can define 'interface Bar' a second time to add additional members to the interface. That's the key to supporting jQuery plugins in a strongly typed way.

So to add support for a given jQuery plugin you're going to need to create a plugin.d.ts file for the plugin you want to use. We use jQuery Templates in our project so here's the jquery.tmpl.d.ts file we created to add support for that plugin:

interface JQuery
{
    tmpl(data?:any,options?:any): JQuery;
    tmplItem(): JQueryTmplItem;
    template(name?:string): ()=>any;
}

interface JQueryStatic
{
    tmpl(template:string,data?:any,options?:any): JQuery;
    tmpl(template:(data:any)=>string,data?:any,options?:any): JQuery;
    tmplItem(element:JQuery): JQueryTmplItem;
    tmplItem(element:HTMLElement): JQueryTmplItem;
    template(name:string,template:any): (data:any)=>string[];
    template(template:any): JQueryTemplateDelegate;
}

interface JQueryTemplateDelegate {
    (jQuery: JQueryStatic, data: any):string[];
}

interface JQueryTmplItem
{
    data:any;
    nodes:HTMLElement[];
    key:number;
    parent:JQueryTmplItem;
}

Breaking this down the first thing we did is to define the methods that get added to the JQuery interface. These let you get intellisense and type checking when you type $('#foo').tmpl(); Next we added methods to the JQueryStatic interface which show up when you type $.tmpl(); And finally the jQuery Templates plugin defines some of its own data structures so we needed to define interfaces for those structures.

Now that we have the additional interfaces definied we just need to reference them from the consuming .ts files. To do that we just add the references below to the top of our .ts file and that's it. For that file, TypeScript will see both the base jQuery methods and the plugin methods. If you use multiple plugins just make sure you refernce all of your individual plugin.d.ts files and you should be good.

/// <reference path="jquery.d.ts"/>
/// <reference path="jquery.tmpl.d.ts" />

Solution 2:

Using a .d.ts declaration file is probably better, but as an alternative you can also use TypeScript's global augmentation and declaration merging to add methods to JQuery's interface. You can place something like the following in any of your TypeScript files:

declare global {
    interface JQuery {
        nameOfPluginMethod(arg: any): JQuery;
    }
}

Solution 3:

Saving a .ts file does not automatically trigger compilation in Visual Studio. You will need to build/rebuild to trigger the compilation.

Declare files (file.d.ts) lets the TypeScript compiler get better type information about the JavaScript libraries you are using from that file. You can have your interfaces defined all in one file, or in multiple files; this should not make any difference. You can also "declare" the types/variables you are using from external libraries using something like:

declare var x: number;

which will tell the compiler to treat x as a number.

Solution 4:

I've been looking for a d.ts for jquery.inputmask and finally created a simple one of my own. It's at

https://github.com/jpirok/Typescript-jquery.inputmask

or you can see the code below.

It won't cover all cases for jquery.inputmask, but will probably handle most.

    ///<reference path="../jquery/jquery.d.ts" />

interface JQueryInputMaskOptions {
    mask?: string;
    alias?: string;
    placeholder?: string;
    repeat?: number;
    greedy?: boolean;
    skipOptionalPartCharacter?: string;
    clearIncomplete?: boolean;
    clearMaskOnLostFocus?: boolean;
    autoUnmask?: boolean;
    showMaskOnFocus?: boolean;
    showMaskOnHover?: boolean;
    showToolTip?: boolean;
    isComplete?: (buffer, options) => {};
    numeric?: boolean;
    radixPoint?: string;
    rightAlignNumerics?: boolean;
    oncomplete?: (value?: any) => void;
    onincomplete?: () => void;
    oncleared?: () => void;
    onUnMask?: (maskedValue, unmaskedValue) => void;
    onBeforeMask?: (initialValue) => void;
    onKeyValidation?: (result) => void;
    onBeforePaste?: (pastedValue) => void;
}

interface inputMaskStatic {
    defaults: inputMaskDefaults;
    isValid: (value: string, options: inputMaskStaticDefaults) => boolean;
    format: (value: string, options: inputMaskStaticDefaults) => boolean;
}

interface inputMaskStaticDefaults {
    alias: string;
}

interface inputMaskDefaults {
    aliases;
    definitions;
}

interface JQueryStatic {
    inputmask: inputMaskStatic;
}

interface JQuery {
    inputmask(action: string): any;
    inputmask(mask: string, options?: JQueryInputMaskOptions): JQuery;
}

Solution 5:

Before creating your own .d.ts file for the plugin, you should check to see whether it's already as a DefinitelyTyped library. For example, using Typings, you can run the command:

typings install dt~bootstrap --global --save

... and without any extra code you'll have access to the various Bootstrap plugins.

If they don't have the plugin you're looking for, consider contributing your own definition.