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 声明会被“提升”到其所在作用域的顶部。这可能导致令人困惑的错误:

🌐 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";

新的 ES6 关键字 let,现在在 TypeScript 中得到支持,用于声明具有更直观“块”语义的变量。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) 为目标时可用。

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) 为目标时可用。

模板字符串

🌐 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

在联合类型中使用 typeofelse

🌐 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 文件。在构建服务器或其他仅希望获得“干净”构建输出的场景中,这可能是不理想的。新的标志 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;
});