Learining TypeScript (一) TypeScript 簡介

Learining TypeScript (一) TypeScript 簡介

一、TypeScript出現的背景    2

二、TypeScript的架構    2

1.    設計目標    2

2.    TypeScript組件    4

三、TypeScript語言特性    4

1、類型    7

2、變量、基本類型和運算符    8

3、流程控制語句    12

4、函數    12

5、類    13

6、接口    14

7、命名空間    15

四、小結    16

 

一、TypeScript出現的背景

在過去的幾年裏,基於JavaScript的Web應用的數量呈幾何級數增長,雖然目前ES6(即ECMAScript2015,從ES6開始採用年份命名)的標準在2015年6月份已經發布了,增添了許多新的特性,例如:模塊和類,以及一些實用的特性,例如Map,Sets,Generates,Promises等等,支持向後兼容,所有的老代碼也可以正常運行,但目前爲止還沒有一個完全支持ES6標準的JavaScript代理,不論是瀏覽器還是服務器環境,所以開發者不得不將ES6代碼向ES5轉義,所以ES6標準的廣泛應用,還需要一個漫長的過程。

爲了解決JavaScript的維護和擴展問題,微軟花了兩年的時間開發出TypeScript,並在2012年10月公佈了第一個公開的版本。它是JavaScript的一個超集,它與現存的JavaScript代碼有非常高的兼容性,任何合法的JavaScript程序都是合法的TypeScript程序,另外它還給大型項目提供一個構建機制,TypeScript(以下簡稱ts)中加入了基於類(Class)的對象、接口和模塊,採用面向對象編程的方式。至此ts就誕生了。

二、TypeScript的架構

    學習的過程就是這樣,當你學習一門新的東西的時候,總是要從基礎知識開始學起,這樣有利於你以後的學習,還有個作用是,給你以後的面試提供很大的幫助。

1、設計目標

我們可以從以下幾點中瞭解到,決定TypeScript作爲程序語言發展到今天這個形態的設計目標和架構:

  • 對JavaScript結構的靜態分析很有可能死錯誤的。微軟(以下簡稱MS)的工程師們認爲爲了防止並排查一些運行時錯誤的最佳方式是,創造一種在編譯期間進行靜態類型分析的強類型語言,因此他們設計了一個語言服務層給開發者提供一些更好的工具。
  • 與現存的JavaScript代碼有非常高的兼容性。TypeScript作爲JavaScript的超集,所有合法的JavaScript程序代碼都是合法的TypeScript代碼。
  • 給大型項目提供一個構建機制。引入了類的對象、接口和模塊,結合最佳的面向對象的編程,使代碼更具可維護性和擴展性。
  • 對於發行版本的代碼,沒有任何運行時開銷。在使用TypeScript時,通常是將程序設計階段和運行階段區分開來的,通俗的講就是說我們在設計程序時使用TypeScript代碼,而運行時呢編譯成JavaScript代碼。爲何要這樣呢?原因很簡單,比如在ts中有接口的概念,在js中是不存在也不支持接口的,那麼ts編譯器在編譯出的js代碼中也就不會聲明也不會模擬這個特性。MS的Engineers提供了一些類似於代碼轉換(將TypeScript特性轉變爲JavaScript代碼實現)和類型擦除(移除靜態類型標記)的組件給ts的編譯器,來產生純淨的JavaScript代碼,類型擦除組件不僅移除了類型的標註,還移除了所有ts代碼的所有的高級語言特性,比如接口等。

    ts在編譯時默認使用ES3標準進行編譯,極大的提高了瀏覽器的兼容性,當然它也支持ES5和ES6的標準。一般情況下使用任意受支持的編譯目標,我們都可以使用ts特性,但部分特性需要ES5或更高的版本版本作爲編譯目標的先決條件。

  • 遵循當前以及未來出現的ECMAScript規範。ts不僅支持現有的js代碼,而且也擁有兼容未來版本的能力,大多數的ts的新增特性都是基於未來的JavaScript提案,這樣也意味着許多ts代碼在將來甚至會變成合法的JavaScript代碼。
  • 成爲跨平臺的開發工具。MS使用Apache協議開源了TypeScript,並且它可以在所有的主流操作系統上安裝和執行。

2、TypeScript組件

TypeScript語言內部主要被分爲三層,每一層又被依次分爲子層或者組件,如下圖所示:

    每一層都有不同的用途:

  • 語言層:實現所有TypeScript的語言特性。
  • 編譯層:執行編譯、類型檢查,然後將TypeScript代碼轉換成JavaScript代碼。
  • 語言服務層:生成信息以幫助編輯器和其他工具來提供更好的輔助特性,比如IntelliSense(智能感知),這是微軟提供的一種代碼檢查與提示服務麼可以在編碼時提供相關信息。
  • IDE整合:爲了利用TypeScript的特性,IDE開發者需要完成一些集成工作。

三、TypeScript語言特性

在介紹TypeScript語言特性之前,首要任務是要搭建開發環境,官方提供了一個在線編輯器http://www.typescriptlang.org/play/ 該編輯器如下圖所示:

編輯器左側編寫TypeScript代碼,右側會自動編譯成JavaScript代碼,當然作爲我們專業的programmer來說,很少會採用這種在線編輯的方式進行開發了,可以下載安裝TypeScript的編譯器,作爲一個.Net Developer來說,我們可以時候用Visual Studio,從VS2015開始,自動集成了TypeScript的開發環境,如果是以下版本,需要去官方下載TypeScript插件,當然我們現在推薦你使用VS2017了。除了這些之外,如果想在其他操作系統中開發,OSX和Linux,那我們可以選擇一些比較流行的編輯器,他們都會有TypeScript插件,比如Visual Studio Code(以下成爲VS Code),Sublime等,這裏我推薦VS Code,它較Visual Studio不同在於前者是IDE集成開發環境,而後者定義爲Eidtor(編輯器),我們不需要去安裝Visual Studio幾十個G的安裝包,而且VS Code已經提供了成千上萬的插件供你使用,你自己也可以去開發自己的插件,如圖所示:

微軟大大一直以來都是那麼的貼心。VS Code提供了各操作系統的版本,可以直接下載使用。

選擇好我們的開發編輯器後,就要安裝TypeScript,需要用到npm命令,但是在使用npm命令前,需要在開發環境中安裝node.js,地址https://nodejs.org,找到對應的版本下載即可。驗證是否安裝成功,可使用

npm –version 查看npm版本

下面我們可以使用命令安裝TypeScript了

npm install -g typescript

(-g)爲全局安裝,但是如果你是OS X用戶,需要使用sudo命令,需要獲取管理員權限,

sudo npm install -g typescript

可以使用 tsc -v 命令驗證是否安裝成功

Ok,至此我們的開發環境就搭建完成了。

現在我們創建一個test.ts文件,保存一個文件夾內,裏面輸入以下代碼:

var t : number = 1;

然後切換到此文件夾內,執行

tsc test.ts

此時可以發現,在同目錄下,會編譯生成一個test.js文件。如圖:

1、類型

我們已經知道,ts是js的超集,ts通過向js增加可選的靜態類型聲明來把js變成強類型語言,可選的靜態類型聲明可約束函數,變量,屬性等程序實體,這樣編譯器和相應的開發工具就可以在開發過程中提供更好的正確性驗證和輔助功能(比如IntellSense)。還可以讓程序員對自己的和其他開發人員的代碼表達他的意圖,另外ts的類型檢測在編譯器運行並且沒有運行時開銷。

比如:

var counter;                    //未知(any)類型

var counter=0;                    //number類型(推斷出的)

var counter:number;            // number類型

var counter:number=0;            // number類型

2、變量、基本類型和運算符

    這部分簡要說明,對ts的基本類型包括boolean、number、string、array、void和所有用戶自定義的enum類型。所有的這些類型都是any類型的子類型。來看幾種特殊的類型的用法:

  • Array 和js一樣,ts允許使用數組,array類型的聲明有兩種寫法,

    第一種:

var list:number[]=[1,2,3];

第二種是使用範型數組類型Array:

var list:Array<number>=[1,2,3];

  • enum 枚舉類型,爲了更好的給一個數字集合命名,默認從0開始,當然你也可以手動設置成員的值,如:

    enum Color{Red,Green,Blue};

    var c:Color=Color.Red;

  • any類型可以表示任意的JavaScript的值。
  • void類型在某種程度上是any類型的對立面,即所有類型不存在的時候,你會在一個沒有返回值得函數中看到他:

    function warnUser:void{

        alert("This is my warning message!");

    }

  • null 和undefined 。在JavaScript中undefined是全局作用域的一個屬性,它會賦值給那些已被聲明但是未被初始化的變量;null是一個字面量(不是全局對象的一個屬性),他可以被賦值給那些表示沒有值的變量。例如:

    var TestStr;            //變量聲明但未初始化

    alert(TestStr);            //顯示undefined

    alert(typeof TestStr);        //顯示undefined

    var TestStr=null;            //變量聲明,並且被賦值爲null

    alert(TestStr);            //顯示null

    alert(typeof TestStr);        //顯示object

    然而在TypeScript中,null和undefined不能當作類型來使用:

    var TestStr:null;         //錯誤,類型錯誤

    var TestStr:undefined;     //錯誤,找不到undefined

  • var、let和const

    在TypeScript中,當聲明一個變量時,可以使用var 、let和const關鍵字:

    var myNum:number=-1;

    let isValid:Boolean=true;

    const appid:string="0FDFD-8989-4CC3-9080-CSDJJFID7HJFD9";

    var 聲明的變量保存在最近的函數作用域中,如果不存在任何函數中則在全局作用域中。

    let聲明的變量保存在最近的比函數作用域小的塊作用域中,如果不存在則在全局作用域中。

    const關鍵字會創建一個保存在創建位置作用域中的常量,可以是全局作用域也可以是塊作用域,這表明const是塊作用的,關於作用域的知識會在後面進一步瞭解。

  • 聯合類型

    TypeScript允許聯合聲明:

    var path:string[]|sring;

    path='/temp/log.xml';

    path=['/temp/log.xml', '/temp/info.xml'];

    path=1;

    此時path=1;會報類型錯誤,因爲在聲明時並未對path進行number的合法聲明。

  • 類型守護

    可以在運行時使用typeof或者instanceof運算符對類型進行驗證。TypeScript語言服務會在if區域尋找這些運算符,然後對應地更改類型:

    var x: any = { /* ... */ };

    if(typeof x === 'string') {

    console.log(x.splice(3, 1)); // 錯誤,'string'上不存在'splice'方法

    }

    // x 依然是 any 類型

    x.foo(); // 合法

    在這段代碼中,我們首先聲明瞭一個any類型的變量x,隨後在運行時通過typeof運算符對x進行了類型檢查。如果x的類型爲string時,我們就會嘗試調用被認爲是x的一個成員的splice方法。TypeScript語言服務可以讀懂在條件語句中使用typeof的用法。TypeScript會自動推斷出x一定是string類型,然後告訴我們splice方法不存於string類型上。這種特性被稱爲類型守護。

  • 類型別名

    TypeScript允許使用type關鍵字聲明類型別名:

    type PrimitiveArray = Array<string|number|boolean>;

    type MyNumber = number;

    type NgScope = ng.IScope;

    type Callback = () => void;

    但是不建議在一個大的項目團隊中使用這種別名,可維護性會降低。

  • 環境聲明

    環境聲明允許在TypeScript 代碼中創建一個不會被編譯到 JavaScript中的變量。這個特性是用來促進與現有 JavaScript 代碼、DOM(文檔對象模型),還有BOM(瀏覽器對象模型)結合而設計的。讓我們看一個例子:

    customConsole.log("A log entry!"); // 錯誤

    如果你嘗試調用customConsole對象上的log方法,TypeScript會告訴我們customConsole對象未被聲明:

    // Cannot find name 'customConsole'

    出現這種情況並不令人意外。但是,有時候我們希望調用一個未被定義的對象上的方法,比如window對象上的console方法。

    console.log("Log Entry!");

    var host = window.location.hostname;

    當訪問 DOM 或 BOM 對象時,我們沒有遇到錯誤,是因爲這些對象已經在一個特殊的 TypeScript 文件(被稱爲聲明文件)中被聲明瞭。可以使用declare操作符創建一個環境聲明。

    在下面這段代碼中,我們會聲明一個被customConsole對象實現的接口。然後使用declare操作符在作用域中增加一個customConsole對象:

    interface ICustomConsole {

    log(arg : string) : void;

    }

    declare var customConsole : ICustomConsole;

    然後就可以在沒有編譯錯誤的情況下使用customConsole:

    customConsole.log("A log entry!"); // 成功

    TypeScript 默認包含一個名爲lib.d.ts的文件,它提供了像 DOM 這種 JavaScript 內置庫的接口聲明。

    使用.d.ts結尾的聲明文件,是用來提高 TypeScript 對第三方庫和像 Node.js 或瀏覽器這種運行時環境的兼容性的。

  • 運算符

    運算符包括:算術運算符、比較運算符、邏輯運算符、位運算符、以及賦值操作符。

    這些運算符在這裏不再贅述,如果有開發的基礎的話,這些應該都會清楚。

3、流程控制語句

包含選擇語句、循環語句和分支語句

if 、if else 、?、switch case 、while 、do while 、for in(類似freach)、for循環,這些語句是我們再熟悉不過的東東了,也不再贅述了吧。

4、函數

關於函數,就像JavaScript一樣,TypeScript可以通過具名或匿名方式創建,根據具體情況,選擇合適的方式聲明即可。如:

function greet(name?:string):string{

    return "Hi"+name;

}//這是一個帶有返回值的具名函數;

//匿名函數

var greet=function(name?:string):string{

    return "Hi"+name;

}

如果不想使用函數的語法,還可以有另一種選擇,使用箭頭(=>)操作符並不適用function關鍵字,如:

var greet=(name?:string):string=>{

    return "Hi"+name;

}

在這個例子中,我們還可以給greet變量添加上匹配匿名函數的類型:

var greet=(name?:string)=>string=function(name:string):string{

    return "Hi"+name;

}

*注意:當處於類的內部時,如果使用箭頭函數(=>)語法將會改變this操作符的工作機制,會在後面的學習中詳細討論。

已經學習瞭如何將一個變量強制描述爲指定形式的函數。這在我們使用回調函數時是十分有用的。

function sume(a:number,b:number,callback(result:number)=>void){

    callback(a+b);

}

5、類

    在ES6中添加了基於類的面向對象編程語法,由於TypeScript是基於ES6的,所以我們可以開始使用基於類的面向對象的語法了。TypeScript的編譯器會負責將TypeScript代碼編譯爲兼容主流瀏覽器和平臺的JavaScript代碼。

class Person {

fullname: string;

constructor(firstname: string, lastname: string) {

this.fullname = firstname + " " + lastname;

}

hello(name?: string) {

if (name) {

return "Hi! " + name + "! My Name is " + this.fullname;

} else {

return "Hi! My Name is" + this.fullname;

}

}

}

 

var person = new Person("Allen", "Choi");

var msg = person.hello("Allen");

alert(msg);// "Hi !Allen! My Name is Allen Choi";

var msgnull = person.hello();

alert(msgnull);//"Hi! My Name is Allen Choi"

在上面的例子中,我們定義了一個Person類,有三個成員:一個fullname屬性,一個構造函數constructor和一個greet方法。當在TypeScript中聲明類時,所有的屬性和方法都是公共的。

當在對象內部訪問對象成員時,我們都加上了this操作符,這表明這是一個成員訪問操作,我們使用new操作符構造了一個Person實例,這會調用類的構造函數,按照定義對實例進行初始化。

爲了兼容ES3和ES5,TypeScript中的類會編譯爲JavaScript中函數

var Person = /** @class */ (function () {

function Person(firstname, lastname) {

this.fullname = firstname + " " + lastname;

}

Person.prototype.hello = function (name) {

if (name) {

return "Hi! " + name + "! My Name is " + this.fullname;

}

else {

return "Hi! My Name is" + this.fullname;

}

};

return Person;

}());

var person = new Person("Allen", "Choi");

var msg = person.hello("Allen");

alert(msg);

var msgnull = person.hello();

alert(msgnull);

6、接口

在TypeScript中,可以使用接口來確保類擁有指定的結構。

interface LoggerInterface {

log(arg: any): void;

}

class Logger implements LoggerInterface {

log(arg) {

if (typeof console.log === "function") {

console.log(arg);

} else {

alert(arg);

}

}

}

在這個例子中,我們定義了一個LoggerInterface接口,和實現了一個Logger的類,Typescript也允許使用接口來約束對象,這樣可以幫助我們避免很多小錯誤。例如下面的例子中:

interface UserInterface {

name: string;

password: string;

}

 

var user: UserInterface = {

name:"",

pasword:"" //password 遺漏錯誤屬性

}

7、命名空間

命名空間,又稱內部模塊,被用於組織一些具有某些內在聯繫的特性和對象。命名空間能夠使代碼結構更清晰,可以使用namespace和export關鍵字,在TypeScript中聲明命名空間。

namespace Geometry {

interface VertorInterface {

/*...*/

}

export interface Vertor2dInterface {

/*...*/

}

export interface Vertor3dInterface {

/*...*/

}

export class Vertor2d implements VertorInterface, Vertor2dInterface {

/*...*/

}

export class Vertor3d implements VertorInterface, Vertor3dInterface {

/*...*/

}

}

 

var vector2dInstance: Geometry.Vertor2dInterface = new Geometry.Vertor2d();

var vector3dInstance: Geometry.Vertor3dInterface = new Geometry.Vertor3d();

在上面例子中,我們聲明瞭一個包含了Vector2d、Vector3d類和VectorInterface、Vector2dInterface、Vector3dInterface接口的命名空間。

*注意:命名空間內的VectorInterface並沒有export關鍵字,所以在命名空間外部,我們訪問不到它。

8、綜合運用

下面是一個使用了模塊、類、函數和類型註解的案例:

module Geometry{

export interface Vector2dInterface{

toArray(callback:(x:number[])=>void):void;

length():number;

normalize();

}

 

export class Vector2d implements Vector2dInterface{

private _x:number;

private _y:number;

constructor(x:number,y:number){

this._x=x;

this._y=y;

}

toArray(callback:(x:number[])=>void):void{

callback([this._x,this._y]);

}

length():number{

return Math.sqrt(this._x*this._x+this._y*this._y);

}

normalize(){

var len=1/this.length();

this._x*=len;

this._y*=len;

}

}

}

 

var vector:Geometry.Vector2dInterface=new Geometry.Vector2d(2,3);

vector.normalize();

vector.toArray(function(vectorasArray:number[]){

alert('x:'+vectorasArray[0]+'y:'+vectorasArray[1]);

})

 

四、小結

    這一部分的內容主要是介紹了一些關於TypeScript的基礎知識,相信有開發經驗的人對這些並不感冒,不管你是用了兩分鐘看完了,還是用了20分鐘看完了,希望你能有一點點的收穫,內容有書本上的內容,也有網上摘的,如有不對的地方,請多包涵指正。從頭看到尾,或許你越來越感覺它的語法以及一些特性,像極了C#,對於.Net 開發人員來說應該是再熟悉不過了。後面的內容會持續更新,也希望自己能堅持下來吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章