從 JavaScript 語法改寫爲 TypeScript 語法,有兩個關鍵點,一點是類成員變量(Field)須要聲明,另外一點是要爲各類東西(變量、參數、函數/方法等)聲明類型。而這兩個點直接引出了兩個關鍵性的問題,有哪些類型?怎樣聲明?javascript
在說 TypeScript 的類型以前,咱們先複習一下 JavaScript 的七種類型:html
undefinedjava
functiones6
booleantypescript
numbersegmentfault
string數組
object閉包
symbol編輯器
這七種類型都是能夠經過 typeof
運算符算出來的,但其中並無咱們常見的 Array
、null
,Date
之類的類型——由於它們其實都是 object
。模塊化
TypeScript 的重要特性之一就是類型,因此 TypeScript 中的類型要講究得多,除了 JavaScript 中的類型以外,還定義了其它一些(不徹底列表)
Array<T>
,或 T[]
,表示 T 類型的數組
null
,空類型,其做用與 strictNullChecks
編譯參數有關
Tuple(元組),形如 [Number, String]
enum T
,定義枚舉類型 T
,可理解爲集中對數值常量進行命名
interface T
,接口,T
是一種接口類型
class T
,類,T
是一種類型
any
,表明任意類型
void
,表示沒有類型,用於聲明函數類型
never
,表示函數不可返回的神奇類型
……
具體的類型這裏就不詳述了,官方 Handbook 的 Basic Type、Interfaces、Classes、Enum、Advanced Types 這幾部分說得很是清楚。
不過仍然有一種類型相關的特性不得不提——泛型。若是隻是說數據類型,純粹的 JSer 們還能夠理解,畢竟類型不是新鮮玩意兒,只是擴展了點種類。可是泛型這個東西,純粹的 JSer 們可能就沒啥概念了。
泛型主要是用一個符號來表示一些類型,只要是符合約束條件(默認無約束)的類型,均可以替換掉這個類型符號來使用,好比
function test<T>(v: T) { console.log(v); } test<boolean>(true); // 顯式指定 T 由 boolean 替代 test("hello"); // 推斷(隱式) T 被 string 替代 test(123); // 推斷(隱式) T 被 number 替代
泛型與強類型相關,即須要進行嚴格的類型檢查,又想少寫類似代碼,因此乾脆用某個符號來代替類型。泛型這個名稱自己可能並非很好理解,可是若是借用 C++ 的「模板」概念,就好理解了。好比上面的泛型函數,根據後面的調用,能夠被解釋爲三個函數,至關於套用模板,用實際類型代替了 T
:
function test(v: boolean) { ... } function test(v: string) { ... } function test(v: number) { ... }
關於泛型,更詳細的內容能夠參考 Handbook 的 Generic 部分。
類型就簡述到這裏,簡單的類型一看就能明白,高級一點的類型咱們之後再開專題來詳述。不過既然選擇使用 TypeScript,必然會用到它的靜態類型特性,那就必須強化識別類型的意識,並養成這樣的習慣。對於純 JSer 來講,這是一個巨大的挑戰。
聲明類型,主要是指聲明變量/常量,函數/方法和類成員的類型。JS 中使用 var 聲明一個變量,ES6 擴展了 let 和 const。這幾種聲明 TypeScript 都支持。要爲變量或者常量指定類型也很簡單,就是在變量/常量名後面加個冒號,再指定類型便可,好比
// # typescript // 聲明函數 pow 是 number 類型,即返回值是 number 類型 // 聲明參數 n 是 number 類型 function pow(n: number): number { return n * n; } // 聲明 test 是無返回值的 function test(): void { for (let i: number = 0; i < 10; i++) { // 聲明 i 是 number console.log(pow(i)); } }
這段代碼演示了對函數類型、參數類型和變量類型地聲明。這相對於 JavaScript 代碼來講,彷佛變得更復雜了。可是考慮下,若是咱們在某處不當心這樣調用了 pow
:
// # javascript let n = "a"; let r = pow(n); // 這裏存在一個潛在的錯誤
JavaScript 不會提早檢查錯誤的,只有在執行到 r = pow(n)
的時候給 r
賦值爲 NaN
。而後若是別處又用到 r
,可能就會形成連鎖錯誤,可能很要調試一陣才把問題找得出來。
不過上面兩行代碼在 TypeScript 裏是通不過轉譯的,它會報告一個類型不匹配的錯誤:
Argument of type 'string' is not assignable to parameter of type 'number'.
這時先來看一段 JavaScript 代碼
// # javascript (es6) class Person { constructor(name) { this._name = name; } get name() { return this._name; } }
這段 JavaScript 代碼若是翻譯成 TypeScript 代碼,會是這樣
// # typescript class Person { private _name: string; public constructor(name: string) { this._name = name; } public get name(): string { return this._name; } }
注意到 private _name: string
,這句話是在聲明類成員變量 _name
。JavaScript 裏是不須要聲明的,對 this._name
賦值,它天然就有了,但在 TypeScript 裏若是不聲明,就會報告屬性不存在的錯誤:
Property '_name' does not exist on type 'Person'.
雖然寫起來麻煩了一點,可是我也能理解 TypeScript 的苦衷。若是沒有這些聲明,tsc 就搞不清楚你在使用 obj.xxxx
或者 this.xxxx
的時候,這個 xxxx
到底確實是你想要添加的屬性名稱呢,仍是你不當心寫錯了的呢?
另外要注意到的是 private
和 public
修飾符。JavaScript 中存在私有成員,爲了實現私有,你們都想了很多辦法,好比閉包。
TypeScript 提供了 private
來修飾私有成員,protected
修改保護(子類可用)成員,public
修飾公共成員。若是不添加修飾符,默認做爲 public
,以兼容 JavaScript 的類成員定義。不過特別須要注意的是,這些修飾符只在 TypeScript 環境(好比轉譯過程)有效,轉譯成 JavaScript 以後,仍然全部成員都是公共訪問權限的。好比上例中的 TypeScript 代碼轉譯出來基本上就是以前的 JavaScript 代碼,其 _name
屬性在外部仍可訪問。
固然在 TypeScript 代碼中,若是外部訪問了 _name
,tsc 是會報告錯誤的
Property '_name' is private and only accessible within class 'Person'.
因此應用內使用 private
徹底沒問題,可是若是你寫的東西須要作爲第三方庫發佈,那就要想一些手段來進行「私有化」了,其手段和 JavaScript 並沒什麼不一樣。
從 JavaScript 語法改寫 TypeScript 語法,咱們來作個簡單的總結:
類成員須要聲明。
變量、函數參數和返回值須要申明類型。
若是全部這些東西都要聲明類型,工做量仍是滿大的,因此我建議:就接口部分聲明類型。也就是說,類成員、函數/方法的參數和返回類型要聲明類型,便於編輯器進行語法提示,局部使用的變量或者箭頭函數,在能明確推導出其類型的時候,能夠不聲明類型。
關注做者的公衆號「邊城客棧」 →