摘要:當你陷在一個中大型項目中時應用日趨成為常態,沒有類型約束類型推斷,總有種牽一發而動全身的危機和束縛。總體而言,這些付出相對于代碼的健壯性和可維護性,都是值得的。目前主流的都為的開發提供了良好的支持,比如和。參考資料中文文檔
文章博客地址:http://pinggod.com/2016/Typescript/
TypeScript 是 JavaScript 的超集,為 JavaScript 的生態增加了類型機制,并最終將代碼編譯為純粹的 JavaScript 代碼。類型機制很重要嗎?最近的一些項目經歷讓我覺得這真的很重要。當你陷在一個中大型項目中時(Web 應用日趨成為常態),沒有類型約束、類型推斷,總有種牽一發而動全身的危機和束縛。Immutable.js 和 Angular 2 都在使用 TypeScript 做開發,它們都是體量頗大的項目,所以我決定嘗試一下 Typescript。此外我們還可以嘗試 Facebook 的 Flow,比較一下兩者的優劣。Typescript 對 ES6 也有良好的支持,目前組內項目使用 Babel 編譯 ES6,這也就自然而然的把 TypeScirpt 和 Flow / babel-plugin-tcomb 放在了對立面,也許下一篇文章就是介紹 Flow 和 babel-plugin-tcomb。
What and Why如果你想對 TypeScript 有更深入的認識,那么推薦你閱讀 Stack Overflow 上的問答 What is TypeScript and why would I use it in place of JavaScript? ,這一節也是對這篇問答的一個簡述。
雖然 JavaScript 是 ECMAScript 規范的標準實現,但并不是所有的瀏覽器都支持最新的 ECAMScript 規范,這也就限制了開發者使用最新的 JavaScript / ECMAScript 特性。TypeScript 同樣支持最新的 ECMAScript 標準,并能將代碼根據需求轉換為 ES 3 / 5 / 6,這也就意味著,開發者隨時可以使用最新的 ECMAScript 特性,比如 module / class / spread operator 等,而無需考慮兼容性的問題。ECMAScript 所支持的類型機制非常豐富,包括:interface、enum、hybird type 等等。
與 TypeScript 相似的工具語言還有很多,它們主要分為兩個陣營,一個是類似 Babel 的陣營,以 JavaScript 的方式編寫代碼,致力于為開發者提供最新的 ECMAScript 特性并將代碼編譯為兼容性的代碼;另一個則是 Coffeescript、Clojure、Dart 等的陣營,它們的語法與 JavaScript 迥然不同,但最終會編譯為 JavaScript。TypeScript 在這兩者之間取得了一種平衡,它既為 JavaScript 增加了新特性,也保持了對 JavaScript 代碼的兼容,開發者幾乎可以直接將 .js 文件重命名為 .ts 文件,就可以使用 TypeScript 的開發環境,這種做法一方面可以減少開發者的遷移成本,一方面也可以讓開發者快速上手 TypeScript。
JavaScript 是一門解釋型語言,變量的數據類型具有動態性,只有執行時才能確定變量的類型,這種后知后覺的認錯方法會讓開發者成為調試大師,但無益于編程能力的提升,還會降低開發效率。TypeScript 的類型機制可以有效杜絕由變量類型引起的誤用問題,而且開發者可以控制對類型的監控程度,是嚴格限制變量類型還是寬松限制變量類型,都取決于開發者的開發需求。添加類型機制之后,副作用主要有兩個:增大了開發人員的學習曲線,增加了設定類型的開發時間。總體而言,這些付出相對于代碼的健壯性和可維護性,都是值得的。
目前主流的 IDE 都為 TypeScript 的開發提供了良好的支持,比如 Visual Studio / VS Code、Atom、Sublime 和 WebStorm。TypeScript 與 IDE 的融合,便于開發者實時獲取類型信息。舉例來說,通過代碼補全功能可以獲取代碼庫中其他函數的信息;代碼編譯完成后,相關信息或錯誤信息會直接反饋在 IDE 中……
在即將發布的 TypeScript 2.0 版本中,將會有許多優秀的特性,比如對 null 和 undefined 的檢查。cannot read property "x" of undefined 和 undefined is not a function 在 JavaScript 中是非常常見的錯誤。在 TypeScript 2.0 中,通過使用 non-nullable 類型可以避免此類錯誤:let x : number = undefined 會讓編譯器提示錯誤,因為 undefined 并不是一個 number,通過 let x : number | undefined = undefined 或 let x : number? = undefined 可以讓 x 是一個 nullable(undefined 或 null) 的值。如果一個變量的類型是 nullable,那么 TypeScript 編譯器就可以通過控制流和類型分析來判定對變量的使用是否安全:
let x : number?; if (x !== undefined) // this line will compile, because x is checked. x += 1; // this line will fail compilation, because x might be undefined. x += 1;
TypeScript 編譯器既可以將 source map 信息置于生成的 .js 文件中,也可以創建獨立的 .map 文件,便于開發者在代碼運行階段設置斷點、審查變量。此外,TypeScript 還可以使用 decorator 攔截代碼,為不同的模塊系統生成模塊加載代碼,解析 JSX 等。
Usage這一節介紹 TypeScirpt 的一些基礎特性,算是拋磚引玉,希望引起大家嘗試和使用 TypeScript 的興趣。首先,從最簡單的類型標注開始:
// 原始值 const isDone: boolean = false; const amount: number = 6; const address: string = "beijing"; const greeting: string = `Hello World`; // 數組 const list: number[] = [1, 2, 3]; const list: Array= [1, 2, 3]; // 元組 const name: [string, string] = ["Sean", "Sun"]; // 枚舉 enum Color { Red, Green, Blue }; const c: Color = Color.Green; // 任意值:可以調用任意方法 let anyTypes: any = 4; anyTypes = "any"; anyTypes = false // 空值 function doSomething (): void { return undefined; } // 類型斷言 let someValue: any = "this is a string"; let strLength: number = (someValue as string).length;
TypeScript 中的 Interface 可以看做是一個集合,這個集合是對對象、類等內部結構的約定:
// 定義接口 Coords // 該接口包含 number 類型的 x,string 類型的 y // 其中 y 是可選類型,即是否包含該屬性無所謂 interface Coords { x: number; y?: string; }; // 定義函數 where // 該函數接受一個 Coords 類型的參數 l function where (l: Coords) { // doSomething } const a = { x: 100 }; const b = { x: 100, y1: "abc" }; // a 擁有 number 類型的 x,可以傳遞給 where where(a); // b 擁有 number 類型的 x 和 string 類型的 y1,可以傳遞給 where where(b); // 下面這種調用方式將會報錯,雖然它和 where(b) 看起來是一致的 // 區別在于這里傳遞的是一個對象字面量 // 對象字面量會被特殊對待并經過額外的屬性檢查 // 如果對象字面量中存在目標類型中未聲明的屬性,則拋出錯誤 where({ x: 100, y1: "abc" }); // 最好的解決方式是為接口添加索引簽名 // 添加如下所示的索引簽名后,對象字面量可以有任意數量的屬性 // 只要屬性不是 x 和 y,其他屬性可以是 any 類型 interface Coords { x: number; y?: string; [propName: string]: any };
上面的代碼演示了接口對對象的約束,此外,接口還常用于約束函數的行為:
// CheckType 包含一個調用簽名 // 該調用簽名聲明了 getType 函數需要接收一個 any 類型的參數,并最終返回一個 string 類型的結果 interface CheckType { (data: any): string; }; const getType: CheckType = (data: any) : string => { return Object.prototype.toString.call(data); } getType("abc"); // => "[object String]"
與老牌強類型語言 C#、Java 相同的是,Interface 也可以用于約束類的行為:
interface ClockConstructor { new (hour: number, minute: number): ClockInterface; } interface ClockInterface { tick(); } function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface { return new ctor(hour, minute); } class DigitalClock implements ClockInterface { constructor(h: number, m: number) { } tick() { console.log("beep beep"); } } class AnalogClock implements ClockInterface { constructor(h: number, m: number) { } tick() { console.log("tick tock"); } } let digital = createClock(DigitalClock, 12, 17); let analog = createClock(AnalogClock, 7, 32);class
除了 ES6 增加的 Class 用法,TypeScript 還增加了 C++、Java 中常見的 public / protected / private 限定符,限定變量或函數的使用范圍。TypeScript 使用的是結構性類型系統,只要兩種類型的成員類型相同,則認為這兩種類型是兼容和一致的,但比較包含 private 和 protected 成員的類型時,只有他們是來自同一處的統一類型成員時才會被認為是兼容的:
class Animal { private name: string; constructor(theName: string) { this.name = theName; } } class Rhino extends Animal { constructor() { super("Rhino"); } } class Employee { private name: string; constructor(theName: string) { this.name = theName; } } let animal = new Animal("Goat"); let rhino = new Rhino(); let employee = new Employee("Bob"); animal = rhino; // Error: Animal and Employee are not compatible animal = employee;
抽象類是供其他類繼承的基類,與接口不同的是,抽象類可以包含成員方法的實現細節,但抽不可以包含抽象方法的實現細節:
abstract class Animal { // 抽象方法 abstract makeSound(): void; // 成員方法 move(): void { console.log("roaming the earch..."); } }function
添加類型機制的 TypeScript 在函數上最可以秀的一塊就是函數重載了:
let suits = ["hearts", "spades", "clubs", "diamonds"]; function pickCard(x: {suit: string; card: number; }[]): number; function pickCard(x: number): {suit: string; card: number; }; function pickCard(x): any { // Check to see if we"re working with an object/array // if so, they gave us the deck and we"ll pick the card if (typeof x == "object") { let pickedCard = Math.floor(Math.random() * x.length); return pickedCard; } // Otherwise just let them pick the card else if (typeof x == "number") { let pickedSuit = Math.floor(x / 13); return { suit: suits[pickedSuit], card: x % 13 }; } } let myDeck = [{ suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }]; let pickedCard1 = myDeck[pickCard(myDeck)]; let pickedCard2 = pickCard(15); console.log("card: " + pickedCard1.card + " of " + pickedCard1.suit); console.log("card: " + pickedCard2.card + " of " + pickedCard2.suit);
編譯器首先會嘗試匹配第一個函數重載的聲明,如果類型匹配成功就執行,否則繼續匹配其他的重載聲明,因此參數的針對性越強的函數重載,越要靠前聲明。
genricsfunction identity(arg: T[]): T[] { console.log(arg.length); return arg; } let myIdentity: { (arg: T[]): T[]} = identity;
上面的代碼展示了泛型的基本用法,這里的
interface IdentityFn {(arg: T[]): T[]; }; let myIdentity: IdentityFn = identity;
如果同一個泛型變量在接口中被反復使用,那么可以在定義接口名的同時聲明泛型變量:
interface IdentityFn{ (arg: T[]): T[]; }; function identity (arg: T[]): T[] { console.log(arg.length); return arg; } let myIdentity: IdentityFn = identity;
在泛型接口之外,還可以使用泛型類,兩者的形式非常類似:
class GenericNumber{ zeroValue: T; add: (x: T, y: T) => T; }
泛型也可以直接繼承接口約束自己的行為:
interface Lengthwise { length: number; } function loggingIdentitytype inference(arg: T): T { console.log(arg.length); return arg; }
TypeScript 主要有兩種類型推斷方式:Best Common Type 和 Contextual Type。我們先介紹 Best Common Type:
let x = [0, 1, null];
對于上面代碼中的變量 x,如果要推斷出它的類型,就必須充分考慮 [0, 1, null] 的類型,所以這里進行類型推斷的順序是從表達式的葉子到根的,也就是先推斷變量 x 的值都包含什么類型,然后總結出 x 的類型,是一種從下往上的推斷過程。
TypeScript 的類型推論也可以按照從上往下的順序進行,這被稱為 Contextual Type:
window.onmousedown = function(mouseEvent) { // Error: Property "button" does not exist ontype "MouseEvent" console.log(mouseEvent.buton); };
在上面的示例中,TypeScript 類型推斷機制會通過 window.onmousedown 函數的類型來推斷右側函數表達式的類型,繼而推斷出 mouseEvent 的類型,這種從上到下的推斷順序就是 Contextual Type 的特征。
這里只對 TypeScript 的特性做簡單的介紹,更詳細的資料請參考以下資料:
TypeScript 官方文檔
TypeScript 中文文檔
TypeScript Language Specification
React and Webpack在 TypeScript 中開發 React 時有以下幾點注意事項:
對 React 文件使用 .tsx 的擴展名
在 tsconfig.json 中使用 compilerOptions.jsx: "react"
使用 typings 類型定義
interface Props { foo: string; } class MyComponent extends React.Component{ render() { return {this.props.foo} } } ; // 正確
TypeScript 的官方文檔中對 React 的開發做了一個簡單的演示,主要包含以下幾個部分:
使用 tsconfig.json 作為 TypeScript 的編譯配置文件
使用 webpack 作為構建工具,需要安裝 webpack、ts-loader 和 source-map-loader
使用 typings 作為代碼提示工具
具體的搭建流程可以參考文檔 React & Webpack,此外,我個人寫過一個 TypeScript & Webpack & React 開發的最小化模板可供各位參考,與之等同的 Babel & Webpack & React 版本。
如果查看模板之后對 import * as React from "react" 的方式有所疑惑,請查看 TypeScript 的負責人 Anders Hejlsberg 在 issue#2242 中的詳細解析。
TypeScript Document
What is TypeScript and why would I use it in place of JavaScript?
TypeScript 中文文檔
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/80012.html
摘要:自帶的內置對象都可以直接在中當作定義好的類型。的內置對象標準提供了常用的內置對象等。在不需要額外引入就可以直接使用這些內置對象用寫不是內置對象的一部分,想要寫時提示,需要引入第三方聲明文件 JavaScript 自帶的內置對象都可以直接在 TypeScript 中當作定義好的類型。 TypeScript 核心庫的定義文件 TypeScript 核心庫的定義文件定義了所有瀏覽器環境需要用...
摘要:安裝通過命令進行安裝創建文件文件名以擴展名結尾編譯通過命令行進行編譯通過運行任務的方式進行編譯下使用顯示運行任務,選擇構建進行編譯。 安裝 通過 npm 命令進行安裝: $ npm i -g typescript 創建 ts 文件 Typescript 文件名以 ts 擴展名結尾: function hello(value: string) { console.log(`hel...
摘要:函數類型函數返回值類型我們可以給每個參數添加類型之后再為函數本身添加返回值類型。能夠根據返回語句自動推斷出返回值類型,所以通常可以省略它匿名函數完整函數類型完整的函數類型包含兩部分參數類型和返回值類型。 函數是 JavaScript 的第一等公民,函數在 JavaScript 中可以實現抽象層、模擬類、信息隱藏和模塊等等。TypeScript 在一定基礎上擴展了函數的功能。 函數類型 ...
摘要:通常會定義為函數的返回值一個類型的變量是沒有生命用處的,因為類型的變量只能賦值為。和有一些區別的,函數沒有返回值時返回類型為的方法,即使沒有寫明語句,也會在函數執行完的時候,隱式地返回一個類型。中新增加的變量聲明方式。 類型注解 類型注解使用 :TypeAnnotation 語法。類型聲明空間中可用的任何內容都可以用作類型注解。 const num: number = 123; fun...
摘要:當滿足以下條件時,枚舉成員被當作是常數不具有初始化函數并且之前的枚舉成員是常數。在這種情況下,當前枚舉成員的值為上一個枚舉成員的值加。但第一個枚舉元素是個例外。枚舉成員使用常數枚舉表達式初始化。 數字類型枚舉 常規枚舉的值都是數字類型,因此被稱為數字類型枚舉: enum Color { Red, Blue, Green } console.log(Color.R...
摘要:接口的作用是聲明變量的結構和方法,但不做具體的實現。這兩個使用場景不同。額外的屬性檢查從字面上的意思看,其實就是對接口未定義的屬性進行檢查。上面的例子,定義了接口,它具有索引簽名。它不會幫你檢查類是否具有某些私有成員。 接口的作用是聲明變量的結構和方法,但不做具體的實現。通常,接口會強制對所有成員進行類型檢查,包括數量和類型: interface Name { first: s...
閱讀 1701·2021-10-09 09:44
閱讀 3267·2021-09-27 13:36
閱讀 1525·2021-09-22 15:33
閱讀 1278·2021-09-22 15:23
閱讀 1164·2021-09-06 15:02
閱讀 1702·2019-08-29 16:14
閱讀 2908·2019-08-29 15:26
閱讀 2412·2019-08-28 18:08