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