IK.AM

@making's tech note


bootstrap-button.jsをTypeScriptで写経してみた

🗃 {Programming/JavaScript/TypeScript}
🗓 Updated at 2012-11-06T17:58:03Z  🗓 Created at 2012-11-06T17:58:03Z   🌎 English Page

TypeScriptでjQueryプラグインを書いてみたいと思ったが、どうやって書くべきかわからなかったのでぐぐったところ

http://typescript.codeplex.com/discussions/398401

You would create your plugin the way you normally do as above... Then you need to extend TypeScripts definition of JQuery to include your added plugin methods. So for the above plugin its simply:

interface JQuery { myPlugin(): JQuery; }

...要は普通にjavascriptで書いてJQueryオブジェクトを返すinterfaceだけ用意しておけばおk、という回答。。

まあクラスを使っているならTypeScriptを使った方が良いだろう、と思って既存のjQueryプラグインをTypeScriptで書いてみた。対象はBootStrapのbuttonプラグイン

書き直すとこんな感じ

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

interface JQuery {
    button(option: ButtonSettings): JQuery;
}

interface ButtonSettings {
    loadingText?: string;
}

module $ {
    class Button {
    public $element: JQuery;
    public options: ButtonSettings;

    constructor (element: HTMLElement, options: ButtonSettings) {
        this.$element = $(element);
        this.options = <ButtonSettings> $.extend({}, $.fn.button.defaults, options);
    }

    setState(state: string): void {
        var d = 'disabled'
        , $el = this.$element
        , data = $el.data()
        , val = $el.is('input') ? 'val' : 'html';
        
        state = state + 'Text';
        data.resetText || $el.data('resetText', $el[val]());

        $el[val](data[state] || this.options[state]);

        // push to event loop to allow forms to submit
        setTimeout(() => {
        state == 'loadingText' ? 
            $el.addClass(d).attr(d, d) :
            $el.removeClass(d).removeAttr(d);
        }, 0);
    }

    toggle(): void {
        var $parent = this.$element.closest('[data-toggle="buttons-radio"]');
        $parent && $parent.find('.active').removeClass('active');
        this.$element.toggleClass('active');
    }
    }

    /* BUTTON PLUGIN DEFINITION
     * ======================== */
    $.fn.button = function(option) {
    return this.each(function() {
        var $this = $(this)
        , data = $this.data('button')
        , options = typeof option == 'object' && option;
        
        if(!data) {
        $this.data('button', (data = new Button(this, options)));
        }
        if (option == 'toggle') {
        data.toggle();
        } else if (option) {
        data.setState(option);
        }
    });
    }
}

プラグインとして定義しているところは素のjavascriptです。。module $.fnにexportする関数として定義してもよいと思ったけど、元のコードに合わせてみた。

コンパイルすると

var $;
(function ($) {
    var Button = (function () {
        function Button(element, options) {
            this.$element = $(element);
            this.options = $.extend({
            }, $.fn.button.defaults, options);
        }
        Button.prototype.setState = function (state) {
            var d = 'disabled';
            var $el = this.$element;
            var data = $el.data();
            var val = $el.is('input') ? 'val' : 'html';

            state = state + 'Text';
            data.resetText || $el.data('resetText', $el[val]());
            $el[val](data[state] || this.options[state]);
            setTimeout(function () {
                state == 'loadingText' ? $el.addClass(d).attr(d, d) : $el.removeClass(d).removeAttr(d);
            }, 0);
        };
        Button.prototype.toggle = function () {
            var $parent = this.$element.closest('[data-toggle="buttons-radio"]');
            $parent && $parent.find('.active').removeClass('active');
            this.$element.toggleClass('active');
        };
        return Button;
    })();    
    $.fn.button = function (option) {
        return this.each(function () {
            var $this = $(this);
            var data = $this.data('button');
            var options = typeof option == 'object' && option;

            if(!data) {
                $this.data('button', (data = new Button(this, options)));
            }
            if(option == 'toggle') {
                data.toggle();
            } else {
                if(option) {
                    data.setState(option);
                }
            }
        });
    };
})($ || ($ = {}));

まあこんなもんか。ちなみにjquery.d.tsの

[x: string]: HTMLElement;

[x: string]: Function;

に直さないとコンパイルできなかった。

module $.fnで定義した場合は

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

interface JQuery {
    button(option: ButtonSettings): JQuery;
}

interface ButtonSettings {
    loadingText?: string;
}

module $.fn {
    class Button {
    public $element: JQuery;
    public options: ButtonSettings;

    constructor (element: HTMLElement, options: ButtonSettings) {
        this.$element = $(element);
        this.options = <ButtonSettings> $.extend({}, $.fn.button.defaults, options);
    }

    setState(state: string): void {
        var d = 'disabled'
        , $el = this.$element
        , data = $el.data()
        , val = $el.is('input') ? 'val' : 'html';
        
        state = state + 'Text';
        data.resetText || $el.data('resetText', $el[val]());

        $el[val](data[state] || this.options[state]);

        // push to event loop to allow forms to submit
        setTimeout(() => {
        state == 'loadingText' ? 
            $el.addClass(d).attr(d, d) :
            $el.removeClass(d).removeAttr(d);
        }, 0);
    }

    toggle(): void {
        var $parent = this.$element.closest('[data-toggle="buttons-radio"]');
        $parent && $parent.find('.active').removeClass('active');
        this.$element.toggleClass('active');
    }
    }

    /* BUTTON PLUGIN DEFINITION
     * ======================== */
    export function button(option: ButtonSettings) {
    return this.each(function() {
        var $this = $(this)
        , data = $this.data('button')
        , options = typeof option == 'object' && option;
        
        if(!data) {
        $this.data('button', (data = new Button(this, options)));
        }
        if (option == 'toggle') {
        data.toggle();
        } else if (option) {
        data.setState(option);
        }
    });
    }
}

でコンパイル結果が

var $;
(function ($) {
    (function (fn) {
        var Button = (function () {
            function Button(element, options) {
                this.$element = $(element);
                this.options = $.extend({
                }, $.fn.button.defaults, options);
            }
            Button.prototype.setState = function (state) {
                var d = 'disabled';
                var $el = this.$element;
                var data = $el.data();
                var val = $el.is('input') ? 'val' : 'html';

                state = state + 'Text';
                data.resetText || $el.data('resetText', $el[val]());
                $el[val](data[state] || this.options[state]);
                setTimeout(function () {
                    state == 'loadingText' ? $el.addClass(d).attr(d, d) : $el.removeClass(d).removeAttr(d);
                }, 0);
            };
            Button.prototype.toggle = function () {
                var $parent = this.$element.closest('[data-toggle="buttons-radio"]');
                $parent && $parent.find('.active').removeClass('active');
                this.$element.toggleClass('active');
            };
            return Button;
        })();        
        function button(option) {
            return this.each(function () {
                var $this = $(this);
                var data = $this.data('button');
                var options = typeof option == 'object' && option;

                if(!data) {
                    $this.data('button', (data = new Button(this, options)));
                }
                if(option == 'toggle') {
                    data.toggle();
                } else {
                    if(option) {
                        data.setState(option);
                    }
                }
            });
        }
        fn.button = button;
    })($.fn || ($.fn = {}));
    var fn = $.fn;

})($ || ($ = {}));

になる。どっちが良いかな?


✒️️ Edit  ⏰ History  🗑 Delete