Quantcast
Channel: 黑暗執行緒
Viewing all articles
Browse latest Browse all 2311

為jQuery Plugin撰寫TypeScript定義檔

$
0
0

TypeScript是強型別的世界,透過預先宣告物件、屬性、方法、介面,在編輯階段提供Intellisense提示、Go Definition、Find All References、Rename... 等編譯式語言才有的功能,而編譯時可預先抓出參數、型別、方法錯誤,降低執行階段發現修復的高昂成本。

開始使用TypeScript一段時間後,一定會發現一項困擾: 雖然DefinitelyTyped計劃已匯集許多常用JavaScript程式庫的TypeScript定義檔,但畢竟無法涵蓋你會用到的每一個JavaScript程式庫,以我自己為例,馬上面對的問題的便是: 我找不到jQuery BlockUI的定義檔。

於是,在程式中要引用block(),blockUI()會出現紅蚯蚓無法編譯。

有一個取巧解法,只要不在全域範圍,在module、interface、class內部可以透過declare指令重新將jQuery、$定義成any型別,declare的變數並不會出現在JavaScript中,純粹讓編譯器假設該變數存在。如此jQuery變成任意型別物件,不管加上什麼屬性、呼叫任何方法都視為合法,但這得付出代價 – 與jQuery相關的Intellisense與編輯檢查從此失效,退回JavaScript時代。

我曾想到一個投機做法,declare var $就好,當需要強型別時寫jQuery("div"),需要用無定義檔方法時寫$("div")。不過身為程式魔人,一直偷雞摸狗下去也不是辦法,還是乖乖學會怎麼為jQuery Plugin寫定義檔吧!

這裡先試寫一個簡單但無聊的jQuery Plugin當成練習目標,為了涵蓋常遇到的各式情境,我刻意加入多種存取API:

$("…").fill(); 元素塗色(用預設顏色)
$("...").fill({ color: "red" }); 元素塗色(指定顏色)
$.fill(); 網頁塗色(用預設顏色)
$.fill({ color: "red" }); 網頁塗色(指定顏色)
$.fill.options.color = "blue"; 修改預設顏色
$.title(); 取得網頁標題
$.title("…"); 設定網頁標題

TypeScript程式碼如下:

/** jquery.fill options */
interface JQFillOptions {
/** fill color */
    color?: string;
}
 
(function ($) {
var defaultOptions: JQFillOptions = {
        color: "red"
    };
//merge option and default option to get fill color
function getColor(options?: JQFillOptions) {
return $.extend({}, defaultOptions, options).color;
    }
//fill background to document.body
    $.fill = function (options?: JQFillOptions) {
        $("body").css("background-color", getColor(options));
    }
//global options
    $.fill.options = defaultOptions;
//fill background for element
    $.fn.fill = function (options?: JQFillOptions) {
returnthis.each(function () {
            $(this).css("background-color", getColor(options));
        });
    };
//get and set document.title
    $.title = function (title?: string) {
if (title)
            document.title = title;
else
return document.title;
    };
})(jQuery);

寫個網頁測試:

<!DOCTYPEhtml>
<htmlxmlns="http://www.w3.org/1999/xhtml">
<head>
<title>jQuery Fill Plugin</title>
<style>
        div { 
            margin: 12px; padding: 6px; width: 100px; 
            color: white;
        }
</style>
</head>
<body>
<div>Test</div>
<div>Test</div>
<scriptsrc="../Scripts/jquery-2.1.1.js"></script>
<script src="jquery.fill.js"></script>
<script>
        $.fill.options.color = "yellow";
        $.fill();
        $("div").first().fill({ color: "blue" })
        .end().last().fill({ color: "green" });
        $.title($.title() + "$$");
</script>
 
</body>
</html>

測試成功!

接著我們把JavaScript抽出來改寫為TypeScript,一如預料,馬上遇到JQuery、JQueryStatic未定義fill、title的錯誤,無法編譯。

為了讓TypeScript認識我們的Plugin,我們需在jquery.fill.ts加入interface JQuery及interface JQueryStatic宣告,TypeScript會將它們合併到JQuery定義檔的同名interface中,如此JQuery會多了.fill()以支援$("…").fill()語法、JQueryStatic會增加.fill()、.title()以支援$.fill()、$.title()。不過有個地方比較麻煩,由於要同時支援$.fill()、$.fill.options兩種存取方式,需多宣告一個interface JQFillStatic,其中包含一個方法(options?: JQFillOptions)以及一個屬性options,而JQueryStatic interface的fill型別為JQFillStatic,如此才能讓$.fill()與$.fill.options都有效。宣告程式如下:

/** jquery.fill options */
interface JQFillOptions {
/** fill color */
    color?: string;
}
interface JQuery {
/**
     * 將元素填滿背景色
     * 
     * @param options 顏色設定,未提供時依預設值
     */
    fill(options?: JQFillOptions): JQuery;
}
interface JQFillStatic {
/**
     * 將document.body填滿背景色
     * 
     * @param options 顏色設定,未提供時依預設值
     */
    (options?: JQFillOptions);
/** 顏色預設值 */
    options?: JQFillOptions;
}
interface JQueryStatic {
    fill?: JQFillStatic;
/** 
     * 取得或設定document.title
     * @param title 要設定的網頁標題,未提供時傳回現有標題
     */
    title(title?: string);
}
 
(function ($) {
///...省略...
})(jQuery);
 

如此,TypeScript就認得我們的Plugin囉~

在以上案例,JQuery Plugin用TypeScript開發,因此JQuery、JQueryStatic宣告直接寫入同一TypeScript檔即可。如果是第三方JavaScript,做法則比照scripts/typings/jquery/jquery.d.ts,要為該JavaScript檔寫一個同檔名的.d.ts。更進一步,還可將你寫好的定義檔貢獻到DefinitelyTyped,分享給開發社群,這才是新時代的好男兒! (遠目)

劍及履及,我的第一個TypeScript定義檔已經被Merge到DefintelyTyped,DefinitelyTyped會自動將其包成NuGet Package,所以jQuery BlockUI現在有TypeScript定義檔囉~

關於撰寫定義檔,TypeScript CodePlex上有篇教學: Writing Definition (.d.ts) Files,如果你也有心參與DefinitelyTyped的定義檔補完計劃,可以參考貢獻指南


Viewing all articles
Browse latest Browse all 2311

Trending Articles