TypeScript 之父簡(jiǎn)介:TS Anders Hejlsberg: Introducing TypeScript

Anders Hejlsberg: Introducing TypeScript

https://channel9.msdn.com/posts/Anders-Hejlsberg-Introducing-TypeScript

image.png
image.png
image.png

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è)長(zhǎng)期存在的編譯上下文中文件隨著時(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è)名字相同的classnamespace可以合并,并且擁有相同的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è)ProgramSourceFile的集合并帶有一系列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è)NodeSymbol是什么?
  • 這個(gè)SymbolType是什么?
  • 在AST的某個(gè)部分里有哪些Symbol是可見的?
  • 某個(gè)函數(shù)聲明的Signature都有哪些?
  • 針對(duì)某個(gè)文件應(yīng)該報(bào)哪些錯(cuò)誤?

TypeChecker計(jì)算所有東西都是“懶惰的”;為了回答一個(gè)問題它僅“解決”必要的信息。

TypeChecker僅會(huì)檢測(cè)和這個(gè)問題有關(guān)的Node,SymbolType,不會(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è)“令牌開始”?!傲钆崎_始”是指更自然的版本,它表示在文件中令牌開始的位置?!巴暾_始”是指從上一個(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ù)來獲?。?/p>

函數(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)注明出處。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容