聯合型別 (Union types)
概述
聯合型別是一種強大的表達方式,用於表示一個值可以是多種型別中的一種。例如,你可能有一個用於執行程式的 API,它接受的命令列引數可以是 string、string[] 或者是一個返回 string 的函式。現在你可以這樣編寫:
tsinterface RunOptions {program: string;commandline: string[] | string | (() => string);}
對聯合型別的賦值非常直觀——任何可以賦值給聯合型別成員的值,都可以賦值給該聯合型別。
tsvar opts: RunOptions = /* ... */;opts.commandline = '-hello world'; // OKopts.commandline = ['-hello', 'world']; // OKopts.commandline = [42]; // Error, number is not string or string[]
當讀取聯合型別時,你可以訪問它們所共有的屬性。
tsif (opts.commandline.length === 0) {// OK, string and string[] both have 'length' propertyconsole.log("it's empty");}
使用型別保護 (Type Guards),你可以輕鬆地操作聯合型別的變數。
tsfunction formatCommandline(c: string | string[]) {if (typeof c === "string") {return c.trim();} else {return c.join(" ");}}
更嚴格的泛型
由於聯合型別能夠表示廣泛的型別場景,我們決定提高某些泛型呼叫的嚴格性。在此之前,像這樣的程式碼(令人驚訝地)可以在沒有錯誤的情況下編譯透過。
tsfunction equal<T>(lhs: T, rhs: T): boolean {return lhs === rhs;}// Previously: No error// New behavior: Error, no best common type between 'string' and 'number'var e = equal(42, "hello");
有了聯合型別,你現在可以在函式宣告處和呼叫處指定期望的行為。
ts// 'choose' function where types must matchfunction choose1<T>(a: T, b: T): T {return Math.random() > 0.5 ? a : b;}var a = choose1("hello", 42); // Errorvar b = choose1<string | number>("hello", 42); // OK// 'choose' function where types need not matchfunction choose2<T, U>(a: T, b: U): T | U {return Math.random() > 0.5 ? a : b;}var c = choose2("bar", "foo"); // OK, c: stringvar d = choose2("hello", 42); // OK, d: string|number
更好的型別推斷
聯合型別還允許在陣列以及其他可能包含多種型別值的集合中進行更好的型別推斷。
tsvar x = [1, "hello"]; // x: Array<string|number>x[0] = "world"; // OKx[0] = false; // Error, boolean is not string or number
let 宣告
在 JavaScript 中,var 宣告會被“提升”到其所在作用域的頂部。這可能會導致令人困惑的 bug。
tsconsole.log(x); // meant to write 'y' here/* later in the same block */var x = "hello";
TypeScript 現在支援新的 ES6 關鍵字 let,它宣告的變數具有更直觀的“塊級”作用域語義。let 變數只能在其宣告之後被引用,並且其作用域限制在定義它的語法塊中。
tsif (foo) {console.log(x); // Error, cannot refer to x before its declarationlet x = "hello";} else {console.log(x); // Error, x is not declared in this block}
let 僅在目標為 ECMAScript 6 (--target ES6) 時可用。
const 宣告
TypeScript 支援的另一種新的 ES6 宣告型別是 const。const 變數不能被重新賦值,並且必須在宣告時進行初始化。這對於那些你不希望在初始化後更改其值的宣告非常有用。
tsconst halfPi = Math.PI / 2;halfPi = 2; // Error, can't assign to a `const`
const 僅在目標為 ECMAScript 6 (--target ES6) 時可用。
模板字串
TypeScript 現在支援 ES6 模板字串。這是一種在字串中嵌入任意表達式的簡單方法。
tsvar name = "TypeScript";var greeting = `Hello, ${name}! Your name has ${name.length} characters`;
當編譯為非 ES6 目標時,字串會被分解處理。
jsvar name = "TypeScript!";var greeting ="Hello, " + name + "! Your name has " + name.length + " characters";
型別保護 (Type Guards)
JavaScript 中的一個常見模式是使用 typeof 或 instanceof 在執行時檢查表示式的型別。TypeScript 現在能夠理解這些條件,並會在 if 塊中使用它們時相應地改變型別推斷。
使用 typeof 測試變數
tsvar x: any = /* ... */;if(typeof x === 'string') {console.log(x.subtr(1)); // Error, 'subtr' does not exist on 'string'}// x is still any herex.unknown(); // OK
在聯合型別和 else 中使用 typeof
tsvar x: string | HTMLElement = /* ... */;if(typeof x === 'string') {// x is string here, as shown above}else {// x is HTMLElement hereconsole.log(x.innerHTML);}
在類和聯合型別中使用 instanceof
tsclass Dog { woof() { } }class Cat { meow() { } }var pet: Dog|Cat = /* ... */;if (pet instanceof Dog) {pet.woof(); // OK}else {pet.woof(); // Error}
類型別名
你現在可以使用 type 關鍵字為型別定義一個別名。
tstype PrimitiveArray = Array<string | number | boolean>;type MyNumber = number;type NgScope = ng.IScope;type Callback = () => void;
類型別名與其原始型別完全相同;它們僅僅是替代名稱。
const enum(完全內聯列舉)
列舉非常有用,但有些程式實際上並不需要生成的程式碼,只需將列舉成員的所有例項內聯為其數值等價物即可。新的 const enum 宣告在型別安全方面與普通 enum 的工作方式相同,但在編譯時會完全抹除。
tsconst enum Suit {Clubs,Diamonds,Hearts,Spades}var d = Suit.Diamonds;
編譯為
jsvar d = 1;
TypeScript 現在也將在可能的情況下計算列舉值。
tsenum MyFlags {None = 0,Neat = 1,Cool = 2,Awesome = 4,Best = Neat | Cool | Awesome}var b = MyFlags.Best; // emits var b = 7;
-noEmitOnError 命令列選項
TypeScript 編譯器的預設行為是:即使存在型別錯誤(例如,嘗試將 string 賦值給 number),仍會生成 .js 檔案。在構建伺服器或其他僅希望從“乾淨”構建中獲取輸出的場景中,這可能是不受歡迎的。新的標記 noEmitOnError 可以防止編譯器在存在任何錯誤時生成 .js 程式碼。
這現在是 MSBuild 專案的預設設定;這使得 MSBuild 的增量構建能夠按預期工作,因為輸出僅在乾淨構建時生成。
AMD 模組名稱
預設情況下,生成的 AMD 模組是匿名的。當使用其他工具(如打包器 r.js)處理生成的模組時,這可能會導致問題。
新的 amd-module name 標籤允許向編譯器傳遞可選的模組名稱。
ts//// [amdModule.ts]///<amd-module name='NamedModule'/>export class C {}
這將導致在呼叫 AMD define 時,為模組分配名稱 NamedModule。
js//// [amdModule.js]define("NamedModule", ["require", "exports"], function(require, exports) {var C = (function() {function C() {}return C;})();exports.C = C;});