TypeScript 1.4
联合类型
概述
联合类型是表达可以是多种类型之一的值的强大方式。例如,你可能有一个用于运行程序的 API,该程序接受命令行作为 string、string[] 或返回 string 的函数。现在你可以这样写:
interface RunOptions {
program: string;
commandline: string[] | string | (() => string);
}对联合类型的赋值非常直观——你可以赋值给联合类型成员的任何值都可以赋值给该联合:
var opts: RunOptions = /* ... */;
opts.commandline = '-hello world'; // 可以
opts.commandline = ['-hello', 'world']; // 可以
opts.commandline = [42]; // 错误,数字不是字符串或字符串数组从联合类型读取时,你可以看到它们共享的任何属性:
if (opts.commandline.length === 0) {
// 可以,string 和 string[] 都有 'length' 属性
console.log("it's empty");
}使用类型守卫,你可以轻松处理联合类型的变量:
function formatCommandline(c: string | string[]) {
if (typeof c === "string") {
return c.trim();
} else {
return c.join(" ");
}
}更严格的泛型
由于联合类型能够表示广泛的类型场景,我们决定提高某些泛型调用的严格性。以前,像这样的代码会(令人惊讶地)编译通过且没有错误:
function equal<T>(lhs: T, rhs: T): boolean {
return lhs === rhs;
}
// 之前:没有错误
// 新行为:错误,'string' 和 'number' 之间没有最佳公共类型
var e = equal(42, "hello");使用联合类型,你现在可以在函数声明点和调用点指定所需的行为:
// 'choose' 函数要求类型匹配
function choose1<T>(a: T, b: T): T {
return Math.random() > 0.5 ? a : b;
}
var a = choose1("hello", 42); // 错误
var b = choose1<string | number>("hello", 42); // 可以
// 'choose' 函数不需要类型匹配
function choose2<T, U>(a: T, b: U): T | U {
return Math.random() > 0.5 ? a : b;
}
var c = choose2("bar", "foo"); // 可以,c: string
var d = choose2("hello", 42); // 可以,d: string|number更好的类型推断
联合类型还允许在数组以及其他可能包含多种值的集合中进行更好的类型推断:
var x = [1, "hello"]; // x: Array<string|number>
x[0] = "world"; // 可以
x[0] = false; // 错误,布尔值不是字符串或数字let 声明
在 JavaScript 中,var 声明会被“提升”到其包含作用域的顶部。这可能导致令人困惑的错误:
console.log(x); // 本想在这里写 'y'
/* 稍后在同一块中 */
var x = "hello";现在 TypeScript 支持新的 ES6 关键字 let,它声明一个具有更直观“块级”语义的变量。let 变量只能在其声明之后被引用,并且作用域限定在其定义的语法块内:
if (foo) {
console.log(x); // 错误,不能在声明前引用 x
let x = "hello";
} else {
console.log(x); // 错误,x 未在此块中声明
}let 仅在面向 ECMAScript 6(--target ES6)时可用。
const 声明
TypeScript 支持的另一种新 ES6 声明类型是 const。const 变量不能被赋值,并且必须在声明时初始化。这对于不希望初始化后改变值的声明很有用:
const halfPi = Math.PI / 2;
halfPi = 2; // 错误,不能赋值给 `const`const 仅在面向 ECMAScript 6(--target ES6)时可用。
模板字符串
TypeScript 现在支持 ES6 模板字符串。这是一种在字符串中嵌入任意表达式的简便方法:
var name = "TypeScript";
var greeting = `Hello, ${name}! Your name has ${name.length} characters`;当编译到 ES6 之前的目标时,字符串会被分解:
var name = "TypeScript!";
var greeting =
"Hello, " + name + "! Your name has " + name.length + " characters";类型守卫
JavaScript 中常见的模式是使用 typeof 或 instanceof 在运行时检查表达式的类型。TypeScript 现在理解这些条件,并在 if 块中使用时相应地更改类型推断。
使用 typeof 测试变量:
var x: any = /* ... */;
if(typeof x === 'string') {
console.log(x.subtr(1)); // 错误,'subtr' 在 'string' 上不存在
}
// 这里 x 仍然是 any
x.unknown(); // 可以将 typeof 与联合类型和 else 一起使用:
var x: string | HTMLElement = /* ... */;
if(typeof x === 'string') {
// 这里 x 是 string,如上所示
}
else {
// 这里 x 是 HTMLElement
console.log(x.innerHTML);
}将 instanceof 与类和联合类型一起使用:
class Dog { woof() { } }
class Cat { meow() { } }
var pet: Dog|Cat = /* ... */;
if (pet instanceof Dog) {
pet.woof(); // 可以
}
else {
pet.woof(); // 错误
}类型别名
你现在可以使用 type 关键字为类型定义别名:
type PrimitiveArray = Array<string | number | boolean>;
type MyNumber = number;
type NgScope = ng.IScope;
type Callback = () => void;类型别名与其原始类型完全相同;它们只是替代名称。
const enum(完全内联的枚举)
枚举非常有用,但有些程序实际上不需要生成的代码,而是希望简单地将所有枚举成员实例内联为其数值等价物。新的 const enum 声明在类型安全方面与常规 enum 相同,但在编译时完全被擦除。
const enum Suit {
Clubs,
Diamonds,
Hearts,
Spades
}
var d = Suit.Diamonds;编译结果正好是:
var d = 1;TypeScript 现在还会在可能时计算枚举值:
enum MyFlags {
None = 0,
Neat = 1,
Cool = 2,
Awesome = 4,
Best = Neat | Cool | Awesome
}
var b = MyFlags.Best; // 输出 var b = 7;-noEmitOnError 命令行选项
TypeScript 编译器的默认行为是即使存在类型错误(例如,尝试将 string 赋值给 number)仍然输出 .js 文件。这在构建服务器或其他只希望从“干净”构建中获取输出的场景下可能不可取。新标志 noEmitOnError 防止编译器在存在任何错误时输出 .js 代码。
这现在是 MSBuild 项目的默认行为;这允许 MSBuild 增量构建按预期工作,因为输出仅在干净构建时生成。
AMD 模块名称
默认情况下,AMD 模块是匿名生成的。当使用其他工具(如打包器,例如 r.js)处理生成的模块时,这可能会导致问题。
新的 amd-module name 标签允许向编译器传递可选的模块名称:
//// [amdModule.ts]
///<amd-module name='NamedModule'/>
export class C {}结果将作为调用 AMD define 的一部分,将名称 NamedModule 分配给模块:
//// [amdModule.js]
define("NamedModule", ["require", "exports"], function(require, exports) {
var C = (function() {
function C() {}
return C;
})();
exports.C = C;
});