TypeScript 1.4

联合类型

¥Union types

概述

¥Overview

联合类型是一种表达值类型为多种类型的强大方法。例如,你可能有一个用于运行程序的 API,该程序将命令行作为 stringstring[] 或返回 string 的函数。你现在可以写入:

¥Union types are a powerful way to express a value that can be one of several types. For example, you might have an API for running a program that takes a commandline as either a string, a string[] or a function that returns a string. You can now write:

ts
interface RunOptions {
program: string;
commandline: string[] | string | (() => string);
}

对联合类型的赋值非常直观 - 任何可以赋值给联合类型成员的值都可以赋值给联合:

¥Assignment to union types works very intuitively — anything you could assign to one of the union type’s members is assignable to the union:

ts
var opts: RunOptions = /* ... */;
opts.commandline = '-hello world'; // OK
opts.commandline = ['-hello', 'world']; // OK
opts.commandline = [42]; // Error, number is not string or string[]

从联合类型读取时,可以看到它们共享的任何属性:

¥When reading from a union type, you can see any properties that are shared by them:

ts
if (opts.commandline.length === 0) {
// OK, string and string[] both have 'length' property
console.log("it's empty");
}

使用类型保护,你可以轻松处理联合类型的变量:

¥Using Type Guards, you can easily work with a variable of a union type:

ts
function formatCommandline(c: string | string[]) {
if (typeof c === "string") {
return c.trim();
} else {
return c.join(" ");
}
}

更严格的泛型

¥Stricter Generics

由于联合类型能够表示各种类型场景,我们决定提高某些泛型调用的严格性。以前,像这样的代码(令人惊讶地)可以编译而不会出现错误:

¥With union types able to represent a wide range of type scenarios, we’ve decided to improve the strictness of certain generic calls. Previously, code like this would (surprisingly) compile without error:

ts
function 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");

使用联合类型,你现在可以在函数声明处和调用处指定所需的行为:

¥With union types, you can now specify the desired behavior at both the function declaration site and the call site:

ts
// 'choose' function where types must match
function choose1<T>(a: T, b: T): T {
return Math.random() > 0.5 ? a : b;
}
var a = choose1("hello", 42); // Error
var b = choose1<string | number>("hello", 42); // OK
// 'choose' function where types need not match
function choose2<T, U>(a: T, b: U): T | U {
return Math.random() > 0.5 ? a : b;
}
var c = choose2("bar", "foo"); // OK, c: string
var d = choose2("hello", 42); // OK, d: string|number

更好的类型推断

¥Better Type Inference

联合类型还允许在数组和其他集合中可能包含多种值的地方进行更好的类型推断:

¥Union types also allow for better type inference in arrays and other places where you might have multiple kinds of values in a collection:

ts
var x = [1, "hello"]; // x: Array<string|number>
x[0] = "world"; // OK
x[0] = false; // Error, boolean is not string or number

let 声明

¥let declarations

在 JavaScript 中,var 声明是 “hoisted” 到其封闭范围的顶部。这可能会导致一些令人困惑的错误:

¥In JavaScript, var declarations are “hoisted” to the top of their enclosing scope. This can result in confusing bugs:

ts
console.log(x); // meant to write 'y' here
/* later in the same block */
var x = "hello";

TypeScript 现已支持新的 ES6 关键字 let,它使用更直观的 “block” 语义声明变量。let 变量只能在声明后引用,并且作用域限定于定义它的语法块:

¥The new ES6 keyword let, now supported in TypeScript, declares a variable with more intuitive “block” semantics. A let variable can only be referred to after its declaration, and is scoped to the syntactic block where it is defined:

ts
if (foo) {
console.log(x); // Error, cannot refer to x before its declaration
let x = "hello";
} else {
console.log(x); // Error, x is not declared in this block
}

let 仅在针对 ECMAScript 6 (--target ES6) 时可用。

¥let is only available when targeting ECMAScript 6 (--target ES6).

const 声明

¥const declarations

TypeScript 中支持的另一种新的 ES6 声明类型是 constconst 变量不能被赋值,并且必须在声明处初始化。这对于你不想在初始化后更改值的声明很有用:

¥The other new ES6 declaration type supported in TypeScript is const. A const variable may not be assigned to, and must be initialized where it is declared. This is useful for declarations where you don’t want to change the value after its initialization:

ts
const halfPi = Math.PI / 2;
halfPi = 2; // Error, can't assign to a `const`

const 仅在针对 ECMAScript 6 (--target ES6) 时可用。

¥const is only available when targeting ECMAScript 6 (--target ES6).

模板字符串

¥Template strings

TypeScript 现在支持 ES6 模板字符串。这些是在字符串中嵌入任意表达式的简便方法:

¥TypeScript now supports ES6 template strings. These are an easy way to embed arbitrary expressions in strings:

ts
var name = "TypeScript";
var greeting = `Hello, ${name}! Your name has ${name.length} characters`;

当编译为 ES6 之前的目标版本时,字符串会被分解:

¥When compiling to pre-ES6 targets, the string is decomposed:

js
var name = "TypeScript!";
var greeting =
"Hello, " + name + "! Your name has " + name.length + " characters";

类型保护

¥Type Guards

JavaScript 中的一种常见模式是在运行时使用 typeofinstanceof 检查表达式的类型。TypeScript 现在可以理解这些条件,并在用于 if 块时相应地更改类型推断。

¥A common pattern in JavaScript is to use typeof or instanceof to examine the type of an expression at runtime. TypeScript now understands these conditions and will change type inference accordingly when used in an if block.

使用 typeof 测试变量:

¥Using typeof to test a variable:

ts
var x: any = /* ... */;
if(typeof x === 'string') {
console.log(x.subtr(1)); // Error, 'subtr' does not exist on 'string'
}
// x is still any here
x.unknown(); // OK

typeof 与联合类型和 else 一起使用:

¥Using typeof with union types and else:

ts
var x: string | HTMLElement = /* ... */;
if(typeof x === 'string') {
// x is string here, as shown above
}
else {
// x is HTMLElement here
console.log(x.innerHTML);
}

instanceof 与类和联合类型一起使用:

¥Using instanceof with classes and union types:

ts
class Dog { woof() { } }
class Cat { meow() { } }
var pet: Dog|Cat = /* ... */;
if (pet instanceof Dog) {
pet.woof(); // OK
}
else {
pet.woof(); // Error
}

类型别名

¥Type Aliases

现在你可以使用 type 关键字为类型定义别名:

¥You can now define an alias for a type using the type keyword:

ts
type PrimitiveArray = Array<string | number | boolean>;
type MyNumber = number;
type NgScope = ng.IScope;
type Callback = () => void;

类型别名与其原始类型完全相同;它们只是替代名称。

¥Type aliases are exactly the same as their original types; they are simply alternative names.

const enum(完全内联枚举)

¥const enum (completely inlined enums)

枚举非常有用,但有些程序实际上并不需要生成的代码,只需将所有枚举成员的实例与其数值等效项内联即可。新的 const enum 声明的工作方式与常规 enum 类似,以确保类型安全,但在编译时会完全清除。

¥Enums are very useful, but some programs don’t actually need the generated code and would benefit from simply inlining all instances of enum members with their numeric equivalents. The new const enum declaration works just like a regular enum for type safety, but erases completely at compile time.

ts
const enum Suit {
Clubs,
Diamonds,
Hearts,
Spades
}
var d = Suit.Diamonds;

编译结果精确为:

¥Compiles to exactly:

js
var d = 1;

TypeScript 现在还会在可能的情况下计算枚举值:

¥TypeScript will also now compute enum values when possible:

ts
enum MyFlags {
None = 0,
Neat = 1,
Cool = 2,
Awesome = 4,
Best = Neat | Cool | Awesome
}
var b = MyFlags.Best; // emits var b = 7;

-noEmitOnError 命令行选项

¥-noEmitOnError commandline option

TypeScript 编译器的默认行为是,如果存在类型错误(例如,尝试将 string 赋值给 number),仍然会生成 .js 文件。在构建服务器或其他只需要 “clean” 构建输出的场景中,这可能是不可取的。新的标志 noEmitOnError 可防止编译器在出现任何错误时生成 .js 代码。

¥The default behavior for the TypeScript compiler is to still emit .js files if there were type errors (for example, an attempt to assign a string to a number). This can be undesirable on build servers or other scenarios where only output from a “clean” build is desired. The new flag noEmitOnError prevents the compiler from emitting .js code if there were any errors.

现在这是 MSBuild 项目的默认设置;这使得 MSBuild 增量构建能够按预期工作,因为输出仅在干净的构建中生成。

¥This is now the default for MSBuild projects; this allows MSBuild incremental build to work as expected, as outputs are only generated on clean builds.

AMD 模块名称

¥AMD Module names

默认情况下,AMD 模块是匿名生成的。当使用其他工具(例如打包器(例如 r.js))处理生成的模块时,这可能会导致问题。

¥By default AMD modules are generated anonymous. This can lead to problems when other tools are used to process the resulting modules like bundlers (e.g. r.js).

新的 amd-module name 标签允许向编译器传递可选模块名称:

¥The new amd-module name tag allows passing an optional module name to the compiler:

ts
//// [amdModule.ts]
///<amd-module name='NamedModule'/>
export class C {}

作为调用 AMD define 的一部分,将导致为模块分配名称 NamedModule

¥Will result in assigning the name NamedModule to the module as part of calling the AMD define:

js
//// [amdModule.js]
define("NamedModule", ["require", "exports"], function(require, exports) {
var C = (function() {
function C() {}
return C;
})();
exports.C = C;
});