Anders Hejlsberg: Introducing TypeScript
https://channel9.msdn.com/posts/Anders-Hejlsberg-Introducing-TypeScript
TypeScript 是 JavaScript 的超集废封,TypeScript結(jié)合了類型檢查和靜態(tài)分析,顯式接口。TypeScript是微軟的開源項(xiàng)目勋功,它是由C#之父Anders Hejlsberg發(fā)起的。
為什么會(huì)有 TypeScript?
JavaScript 只是一個(gè)腳本語言虱朵,并非真正設(shè)計(jì)用于開發(fā)大型 Web 應(yīng)用蛮穿,JavaScript 沒有提供類和模塊等概念,對(duì)于一個(gè)真正的應(yīng)用開發(fā)驻呐,TypeScript 擴(kuò)展JavaScript 并實(shí)現(xiàn)了這些特性灌诅。TypeScript 主要特點(diǎn)包括:
- TypeScript 是微軟推出的開源語言芳来,使用 Apache 授權(quán)協(xié)議
- TypeScript 是 JavaScript 的超集.
- TypeScript 增加了可選類型、類和模塊
- TypeScript 可以編譯成可讀的猜拾、標(biāo)準(zhǔn)的 JavaScript
- TypeScript 支持開發(fā)大規(guī)模 JavaScript 應(yīng)用即舌,支持所有瀏覽器,主機(jī)和操作系統(tǒng)
- TypeScript 設(shè)計(jì)用于開發(fā)大型應(yīng)用挎袜,并保證編譯后的 JavaScript 代碼兼容性
- TypeScript 擴(kuò)展了 JavaScript 的語法顽聂,因此已有的 JavaScript 代碼可直接與 TypeScript 一起運(yùn)行無需更改
- TypeScript 文件擴(kuò)展名是 ts,而 TypeScript 編譯器會(huì)編譯成 js 文件
- TypeScript 語法與 JScript .NET 相同
- TypeScript 非常易學(xué)和易于理解
語言特性
類
接口
模塊
類型注解
編譯時(shí)類型檢查
Arrow 函數(shù) (類似 C# 的 Lambda 表達(dá)式)
JavaScript 與 TypeScript 的區(qū)別
TypeScript 是 JavaScript 的超集盯仪,擴(kuò)展了 JavaScript 的語法芜飘,因此現(xiàn)有的 JavaScript 代碼無需做任何修改便可與TypeScript一起使用,TypeScript 通過類型注解提供編譯時(shí)的靜態(tài)類型檢查磨总。TypeScript 可處理已有的 JavaScript 代碼嗦明,并只對(duì)其中的 TypeScript 代碼進(jìn)行編譯。
-
核心TypeScript編譯器
-
語法分析器(Parser):
以一系列原文件開始, 根據(jù)語言的語法, 生成抽象語法樹(AST)
-
聯(lián)合器(Binder):
使用一個(gè)
Symbol
將針對(duì)相同結(jié)構(gòu)的聲明聯(lián)合在一起(例如:同一個(gè)接口或模塊的不同聲明蚪燕,或擁有相同名字的函數(shù)和模塊)娶牌。這能幫助類型系統(tǒng)推導(dǎo)出這些具名的聲明。 -
類型解析器與檢查器(Type resolver / Checker):
解析每種類型的構(gòu)造馆纳,檢查讀寫語義并生成適當(dāng)?shù)脑\斷信息诗良。
-
生成器(Emitter):
從一系列輸入文件(.ts和.d.ts)生成輸出,它們可以是以下形式之一:JavaScript(.js)鲁驶,聲明(.d.ts)鉴裹,或者是source maps(.js.map)。
-
預(yù)處理器(Pre-processor):
“編譯上下文”指的是某個(gè)“程序”里涉及到的所有文件钥弯。上下文的創(chuàng)建是通過檢查所有從命令行上傳入編譯器的文件径荔,按順序,然后再加入這些文件直接引用的其它文件或通過
import
語句和/// <reference path=... />
標(biāo)簽間接引用的其它文件脆霎。
-
沿著引用圖走下來你會(huì)發(fā)現(xiàn)它是一個(gè)有序的源文件列表总处,它們組成了整個(gè)程序。 當(dāng)解析導(dǎo)出(import)的時(shí)候睛蛛,會(huì)優(yōu)先選擇“.ts”文件而不是“.d.ts”文件鹦马,以確保處理的是最新的文件。 編譯器會(huì)進(jìn)行與Nodejs相似的流程來解析導(dǎo)入忆肾,沿著目錄鏈查找與將要導(dǎo)入相匹配的帶.ts或.d.ts擴(kuò)展名的文件荸频。 導(dǎo)入失敗不會(huì)報(bào)error,因?yàn)榭赡芤呀?jīng)聲明了外部模塊客冈。
-
獨(dú)立編譯器(tsc):
批處理編譯命令行界面旭从。主要處理針對(duì)不同支持的引擎讀寫文件(比如:Node.js)。
-
語言服務(wù):
“語言服務(wù)”在核心編譯器管道上暴露了額外的一層郊酒,非常適合類編輯器的應(yīng)用遇绞。
語言服務(wù)支持一系列典型的編輯器操作比如語句自動(dòng)補(bǔ)全,函數(shù)簽名提示燎窘,代碼格式化和突出高亮摹闽,著色等。 基本的重構(gòu)功能比如重命名褐健,調(diào)試接口輔助功能比如驗(yàn)證斷點(diǎn)付鹿,還有TypeScript特有的功能比如支持增量編譯(在命令行上使用--watch
)。 語言服務(wù)是被設(shè)計(jì)用來有效的處理在一個(gè)長期存在的編譯上下文中文件隨著時(shí)間改變的情況蚜迅;在這樣的情況下舵匾,語言服務(wù)提供了與其它編譯器接口不同的角度來處理程序和源文件。
請(qǐng)參考 [[Using the Language Service API]] 以了解更多詳細(xì)內(nèi)容谁不。
數(shù)據(jù)結(jié)構(gòu)
-
Node:
抽象語法樹(AST)的基本組成塊坐梯。通常
Node
表示語言語法里的非終結(jié)符;一些終結(jié)符保存在語法樹里比如標(biāo)識(shí)符和字面量刹帕。 -
SourceFile:
給定源文件的AST吵血。
SourceFile
本身是一個(gè)Node
;它提供了額外的接口用來訪問文件的源碼偷溺,文件里的引用蹋辅,文件里的標(biāo)識(shí)符列表和文件里的某個(gè)位置與它對(duì)應(yīng)的行號(hào)與列號(hào)的映射。 -
Program:
SourceFile
的集合和一系列編譯選項(xiàng)代表一個(gè)編譯單元挫掏。Program
是類型系統(tǒng)和生成代碼的主入口侦另。 -
Symbol:
具名的聲明。
Symbols
是做為聯(lián)合的結(jié)果而創(chuàng)建尉共。Symbols
連接了樹里的聲明節(jié)點(diǎn)和其它對(duì)同一個(gè)實(shí)體的聲明褒傅。Symbols
是語義系統(tǒng)的基本構(gòu)建塊。 -
Type:
Type
是語義系統(tǒng)的其它部分袄友。Type
可能被命名(比如樊卓,類和接口),或匿名(比如杠河,對(duì)象類型)碌尔。 -
Signature:
一共有三種
Signature
類型:調(diào)用簽名(call),構(gòu)造簽名(construct)和索引簽名(index)券敌。
編譯過程概述
整個(gè)過程從預(yù)處理開始唾戚。 預(yù)處理器會(huì)算出哪些文件參與編譯,它會(huì)去查找如下引用(/// <reference path=... />
標(biāo)簽和import
語句)待诅。
語法分析器(Parser)生成抽象語法樹(AST)Node
. 這些僅為用戶輸出的抽象表現(xiàn)叹坦,以樹的形式。 一個(gè)SourceFile
對(duì)象表示一個(gè)給定文件的AST并且?guī)в幸恍╊~外的信息如文件名及源文件內(nèi)容卑雁。
然后募书,聯(lián)合器(Binder)處理AST節(jié)點(diǎn)绪囱,結(jié)合并生成Symbols
。 一個(gè)Symbol
會(huì)對(duì)應(yīng)到一個(gè)命名實(shí)體莹捡。 這里有個(gè)一微妙的差別鬼吵,幾個(gè)聲明節(jié)點(diǎn)可能會(huì)是名字相同的實(shí)體。 也就是說篮赢,有時(shí)候不同的Node
具有相同的Symbol
齿椅,并且每個(gè)Symbol
保持跟蹤它的聲明節(jié)點(diǎn)。 比如启泣,一個(gè)名字相同的class
和namespace
可以合并涣脚,并且擁有相同的Symbol
。 聯(lián)合器也會(huì)處理作用域寥茫,以確保每個(gè)Symbol
都在正確的封閉作用域里創(chuàng)建遣蚀。
生成SourceFile
(還帶有它的Symbols
們)是通過調(diào)用createSourceFile
API。
到目前為止纱耻,Symbol
代表的命名實(shí)體可以在單個(gè)文件里看到妙同,但是有些聲明可以從多文件合并,因此下一步就是構(gòu)建一個(gè)全局的包含所有文件的視圖膝迎,也就是創(chuàng)建一個(gè)Program
粥帚。
一個(gè)Program
是SourceFile
的集合并帶有一系列CompilerOptions
。 通過調(diào)用createProgram
API來創(chuàng)建Program
限次。
通過一個(gè)Program
實(shí)例創(chuàng)建TypeChecker
芒涡。
TypeChecker
是TypeScript類型系統(tǒng)的核心。 它負(fù)責(zé)計(jì)算出不同文件里的Symbols
之間的關(guān)系卖漫,將Type
賦值給Symbol
费尽,并生成任何語義Diagnostic
(比如:error)。
TypeChecker
首先要做的是合并不同的SourceFile
里的Symbol
到一個(gè)單獨(dú)的視圖羊始,創(chuàng)建單一的Symbol
表旱幼,合并所有普通的Symbol
(比如:不同文件里的namespace
)。
在原始狀態(tài)初始化完成后突委,TypeChecker
就可以解決關(guān)于這個(gè)程序的任何問題了柏卤。 這些“問題”可以是:
- 這個(gè)
Node
的Symbol
是什么? - 這個(gè)
Symbol
的Type
是什么匀油? - 在AST的某個(gè)部分里有哪些
Symbol
是可見的缘缚? - 某個(gè)函數(shù)聲明的
Signature
都有哪些? - 針對(duì)某個(gè)文件應(yīng)該報(bào)哪些錯(cuò)誤敌蚜?
TypeChecker
計(jì)算所有東西都是“懶惰的”桥滨;為了回答一個(gè)問題它僅“解決”必要的信息。
TypeChecker
僅會(huì)檢測(cè)和這個(gè)問題有關(guān)的Node
,Symbol
或Type
齐媒,不會(huì)檢測(cè)額外的實(shí)體蒲每。
對(duì)于一個(gè)Program
同樣會(huì)生成一個(gè)Emitter
。
Emitter
負(fù)責(zé)生成給定SourceFile
的輸出喻括;它包括:.js
邀杏,.jsx
,.d.ts
和.js.map
双妨。
術(shù)語
完整開始/令牌開始(Full Start/Token Start)
令牌本身就具有我們稱為一個(gè)“完整開始”和一個(gè)“令牌開始”淮阐《T模“令牌開始”是指更自然的版本刁品,它表示在文件中令牌開始的位置『评眩“完整開始”是指從上一個(gè)有意義的令牌之后掃描器開始掃描的起始位置挑随。當(dāng)關(guān)心瑣事時(shí),我們往往更關(guān)心完整開始勒叠。
函數(shù) | 描述 |
---|---|
ts.Node.getStart |
取得某節(jié)點(diǎn)的第一個(gè)令牌起始位置兜挨。 |
ts.Node.getFullStart |
取得某節(jié)點(diǎn)擁有的第一個(gè)令牌的完整開始。 |
瑣碎內(nèi)容(Trivia)
語法的瑣碎內(nèi)容代表源碼里那些對(duì)理解代碼無關(guān)緊要的內(nèi)容眯分,比如空白拌汇,注釋甚至一些沖突的標(biāo)記。
因?yàn)楝嵥閮?nèi)容不是語言正常語法的一部分(不包括ECMAScript API規(guī)范)并且可能在任意2個(gè)令牌中的任意位置出現(xiàn)弊决,它們不會(huì)包含在語法樹里噪舀。但是,因?yàn)樗鼈儗?duì)于像重構(gòu)和維護(hù)高保真源碼很重要飘诗,所以需要的時(shí)候還是能夠通過我們的APIs訪問与倡。
因?yàn)?code>EndOfFileToken后面可以沒有任何內(nèi)容(令牌和瑣碎內(nèi)容),所有瑣碎內(nèi)容自然地在非瑣碎內(nèi)容之前昆稿,而且存在于那個(gè)令牌的“完整開始”和“令牌開始”之間纺座。
雖然這個(gè)一個(gè)方便的標(biāo)記法來說明一個(gè)注釋“屬于”一個(gè)Node
。比如溉潭,在下面的例子里净响,可以明顯看出genie
函數(shù)擁有兩個(gè)注釋:
var x = 10; // This is x.
/**
* Postcondition: Grants all three wishes.
*/
function genie([wish1, wish2, wish3]: [Wish, Wish, Wish]) {
while (true) {
}
} // End function
這是盡管事實(shí)上,函數(shù)聲明的完整開始是在var x = 10;
后喳瓣。
我們依據(jù)Roslyn's notion of trivia ownership處理注釋所有權(quán)别惦。通常來講,一個(gè)令牌擁有同一行上的所有的瑣碎內(nèi)容直到下一個(gè)令牌開始夫椭。任何出現(xiàn)在這行之后的注釋都屬于下一個(gè)令牌。源文件的第一個(gè)令牌擁有所有的初始瑣碎內(nèi)容,并且最后面的一系列瑣碎內(nèi)容會(huì)添加到end-of-file
令牌上扰付。
對(duì)于大多數(shù)普通用戶堤撵,注釋是“有趣的”瑣碎內(nèi)容。屬于一個(gè)節(jié)點(diǎn)的注釋內(nèi)容可以通過下面的函數(shù)來獲扔疠骸:
函數(shù) | 描述 |
---|---|
ts.getLeadingCommentRanges |
提供源文件和一個(gè)指定位置实昨,返回指定位置后的第一個(gè)換行與令牌之間的注釋的范圍(與ts.Node.getFullStart 配合會(huì)更有用)。 |
ts.getTrailingCommentRanges |
提供源文件和一個(gè)指定位置洁奈,返回到指定位置后第一個(gè)換行為止的注釋的范圍(與ts.Node.getEnd 配合會(huì)更有用)。 |
做為例子婉弹,假設(shè)有下面一部分源代碼:
debugger;/*hello*/
//bye
/*hi*/ function
function
關(guān)鍵字的完整開始是從/*hello*/
注釋睬魂,但是getLeadingCommentRanges
僅會(huì)返回后面2個(gè)注釋:
d e b u g g e r ; / * h e l l o * / _ _ _ _ _ [CR] [NL] _ _ _ _ / / b y e [CR] [NL] _ _ / * h i * / _ _ _ _ f u n c t i o n
↑ ↑ ↑ ↑ ↑
完整開始 查找 第一個(gè)注釋 第二個(gè)注釋 令牌開始
開始注釋
適當(dāng)?shù)兀?code>debugger語句后調(diào)用getTrailingCommentRanges
可以提取出/*hello*/
注釋镀赌。
如果你關(guān)心令牌流的更多信息氯哮,createScanner
也有一個(gè)skipTrivia
標(biāo)記,你可以設(shè)置成false
商佛,然后使用setText
/setTextPos
來掃描文件里的不同位置喉钢。
參考資料
TypeScript入門指南(JavaScript的超集)
https://tutorialzine.com/2016/07/learn-typescript-in-30-minutes
TypeScript Handbook(中文版):
http://www.runoob.com/manual/gitbook/TypeScript/_book/index.html
http://www.tslang.cn/#download-links
有人說:“TypeScript 讓 JavaScript 又變成了 Java,而我們不需要另一個(gè) Java良姆,所以我們不需要 TypeScript“肠虽。這樣說的人一定不知道,TypeScript 的類型系統(tǒng)中有:Intersection TypesUnion Types & Discriminated Unions (aka "Algebraic Data Types.")String Literal TypesPolymorphic this TypesIndex TypesMapped Types...這些吧玛追。特別是 Generic Types 組合上 Mapped Types 描述能力爆表税课。如果你的代碼超過 1000 行闲延,而且你不打算浪費(fèi)時(shí)間,那么試試 TypeScript韩玩。當(dāng)然前提是你是有經(jīng)驗(yàn)的開發(fā)人員垒玲,如果是編程初學(xué)者,建議還是先從 JavaScript 開始找颓。
作者:林建入
鏈接:https://www.zhihu.com/question/21879449/answer/233768634
來源:知乎
著作權(quán)歸作者所有合愈。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處击狮。