背景
? ? ? ? 總所周知,TS需要編譯成JS才能在瀏覽器上跑,TS也能夠調(diào)用JS的方法。但是,有一個(gè)現(xiàn)成的第三方JS類庫擺在面前,讓它如何能在TS中使用,很多剛?cè)肟拥呐笥淹坏闷溟T而入。下面我就講講我的辦法吧。
1、直接翻譯
? ? ? ? 看了上面4個(gè)字,大家可能不會(huì)不約而同地眉頭一皺,立刻開噴:“喔靠,餿主意!”。我承認(rèn),這的確是個(gè)笨辦法,但是我的確這么做過。笨辦法也是辦法,重劍無鋒,大巧不工。
? ? ? ? 對于一些JS庫只有不到200-300行代碼,直接翻譯成TS又何妨呢?自己用TS重寫,跟找typings(很多不一定能找到)或者自己寫*.d.ts的時(shí)間其實(shí)差不了多少。自己動(dòng)手,也能加深對庫的理解,夯實(shí)自己。
2、頁面引入 + 全局定義
? ? ? ? 如果對第一條不滿意,不用著急,咱還有別的辦法。第二招,其實(shí)也很簡單粗暴。所謂“頁面引入”就是最常規(guī)的用<script>標(biāo)簽把js直接在html頁面引入,然后“全局定義”,就是聲明一個(gè)變量跟JS的全局變量名或者方法名一樣。
? ? ? ? 下面用大家非常熟悉的jQuery舉例吧。
頁面里直接用<script>標(biāo)簽引入jquery.js
<html>
<head>
<script type="text/javascript" src="js/jquery.min.js"></script>
</head>
<body>
......
TS中聲明全局變量
$,然后就可以直接開擼
declare var $;
class App {
init(){
$("#msg").html("jquery引用成功!");
}
}
? ? ? ? 簡單粗暴的副作用就是不夠優(yōu)雅,聲明的全局變量$的類型只能是any,這意味著還是弱類型變量,因此IDE中沒有任何的方法和屬性還有類型的提示。因而,完全失去了寫TS的樂趣。下面重點(diǎn)說說怎樣做到優(yōu)雅。
3、類型定義文件(.d.ts)
? ? ? ? 相信閣下如果看本文之前百度過,肯定看過這種方法。說明,這是主流。所以,我也準(zhǔn)備重點(diǎn)詳細(xì)地介紹一下。
- 首先,什么是類型定義文件?
應(yīng)該有點(diǎn)類似于C++后綴名為.h的頭文件,用于聲明JS主文件的函數(shù)接口和變量類型的定義,并且把這些定義暴露給TS使用。有了.d.ts的定義之后,TS就能感知到JS庫相關(guān)的代碼提示了。
- 接著,如何得到.d.ts文件?
1.可以先去DefinitelyTyped找找碰碰運(yùn)氣,一般很流行的庫也許會(huì)有。
2.用npm的typings查找獲取的,其實(shí)跟上面沒什么兩樣。
3.網(wǎng)上還能還有一些工具,據(jù)說根據(jù)js庫能夠反向生成.d.ts。我試了都不靠譜,如果是C#生成.d.ts的成功率我是敢打包票,但是對JS我就只能呵呵了。
4.最后,“拿來主義”都不行的情況下,只有自己動(dòng)手豐衣足食了。
自己做.d.ts之前,先分析一下js庫的代碼,看看自己需要什么接口就定義什么。那些自己用不著的大可放過。
例如,我早前做的marked.js,節(jié)選一些代碼:
marked.options =
marked.setOptions = function(opt) {
merge(marked.defaults, opt);
return marked;
};
marked.getDefaults = function () {
return {
baseUrl: null,
breaks: false,
gfm: true,
headerIds: true,
headerPrefix: '',
highlight: null,
langPrefix: 'language-',
mangle: true,
pedantic: false,
renderer: new Renderer(),
sanitize: false,
sanitizer: null,
silent: false,
smartLists: false,
smartypants: false,
tables: true,
xhtml: false
};
};
marked.defaults = marked.getDefaults();
/**
* Expose
*/
marked.Parser = Parser;
marked.parser = Parser.parse;
marked.Renderer = Renderer;
marked.TextRenderer = TextRenderer;
marked.Lexer = Lexer;
marked.lexer = Lexer.lex;
marked.InlineLexer = InlineLexer;
marked.inlineLexer = InlineLexer.output;
marked.Slugger = Slugger;
marked.parse = marked;
這一段,很直觀地看出它有什么接口可以暴露出來了。其實(shí),我只需要一個(gè)parse的方法足矣。那么,我的marked.d.ts就會(huì)這么寫。
interface MDOption {
baseUrl?: string;
breaks?: boolean;
gfm?: boolean;
headerIds?: boolean;
headerPrefix?: string;
highlight?: any;
langPrefix?: string;
mangle?: boolean;
pedantic?: boolean;
sanitize?: boolean;
sanitizer?: any;
silent?: boolean;
smartLists?: boolean;
smartypants?: boolean;
tables?: boolean;
xhtml?: boolean;
}
interface MarkDown {
parse(src: string, opt?: MDOption, callback?: Function);
}
declare module "marked" {
export = marked;
}
declare var marked: MarkDown;
方法定義只有MarkDown.parse一個(gè),外加一個(gè)MDOption的結(jié)構(gòu)定義。這樣,我在TS調(diào)用就能用得很舒服了!至于,最后幾行的declare為什么那么寫?那是TS語法的套路,我這里就不多做解釋,能用就行。好奇寶寶們可以進(jìn)入傳送門深入理解一下。
最后說一下,.d.ts怎么使用。直接上代碼:
///<reference path="../typings/marked.d.ts" />
import * as marked from "marked";
var md_html = marked.parse("### Hello TypeScript");
1.引用路徑
2.引入模塊
3.使用方法
很簡單的三行代碼
至于,主體庫mark.js怎么引入頁面,方法很多,八仙過海各顯神通。我用的是require.js。
require.config({
paths: {
'jquery': '//g.alicdn.com/sj/lib/jquery/dist/jquery.min',
'marked': '/js/lib/marked.min'
}
});
OK,講完,收工。