联合类型
¥Union types
概述
¥Overview
联合类型是一种表达值类型为多种类型的强大方法。例如,你可能有一个用于运行程序的 API,该程序将命令行作为 string
、string[]
或返回 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'; // OKopts.commandline = ['-hello', 'world']; // OKopts.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' propertyconsole.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 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
更好的类型推断
¥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"; // OKx[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 declarationlet 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 声明类型是 const
。const
变量不能被赋值,并且必须在声明处初始化。这对于你不想在初始化后更改值的声明很有用:
¥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 中的一种常见模式是在运行时使用 typeof
或 instanceof
检查表达式的类型。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 herex.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 hereconsole.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;});