TypeScript 2.0

可识别空值和未定义的类型

🌐 Null- and undefined-aware types

TypeScript 有两种特殊类型,Null 和 Undefined,它们的值分别是 nullundefined。以前无法显式命名这些类型,但现在无论类型检查模式如何,都可以使用 nullundefined 作为类型名。

🌐 TypeScript has two special types, Null and Undefined, that have the values null and undefined respectively. Previously it was not possible to explicitly name these types, but null and undefined may now be used as type names regardless of type checking mode.

类型检查器以前认为 nullundefined 可以分配给任何类型。实际上,nullundefined每种 类型的有效值,并且无法专门将它们排除(因此也无法检测它们的错误使用)。

🌐 The type checker previously considered null and undefined assignable to anything. Effectively, null and undefined were valid values of every type and it wasn’t possible to specifically exclude them (and therefore not possible to detect erroneous use of them).

--strictNullChecks

strictNullChecks 切换到新的严格空值检查模式。

在严格的空值检查模式下,nullundefined 属于每种类型的域,只能赋值给它们自身和 any(唯一的例外是 undefined 也可以赋值给 void)。 因此,在常规类型检查模式下 TT | undefined 被认为是同义的(因为 undefined 被认为是任何 T 的子类型),但在严格类型检查模式下它们是不同的类型,并且只有 T | undefined 允许 undefined 值。TT | null 的关系也是同样的情况。

🌐 In strict null checking mode, the null and undefined values are not in the domain of every type and are only assignable to themselves and any (the one exception being that undefined is also assignable to void). So, whereas T and T | undefined are considered synonymous in regular type checking mode (because undefined is considered a subtype of any T), they are different types in strict type checking mode, and only T | undefined permits undefined values. The same is true for the relationship of T to T | null.

示例

🌐 Example

ts
// Compiled with --strictNullChecks
let x: number;
let y: number | undefined;
let z: number | null | undefined;
x = 1; // Ok
y = 1; // Ok
z = 1; // Ok
x = undefined; // Error
y = undefined; // Ok
z = undefined; // Ok
x = null; // Error
y = null; // Error
z = null; // Ok
x = y; // Error
x = z; // Error
y = x; // Ok
y = z; // Error
z = x; // Ok
z = y; // Ok

使用前赋值检查

🌐 Assigned-before-use checking

在严格的空值检查模式下,编译器要求对不包含 undefined 类型的局部变量的每一次引用之前,在每条可能的前置代码路径中都必须对该变量进行赋值。

🌐 In strict null checking mode the compiler requires every reference to a local variable of a type that doesn’t include undefined to be preceded by an assignment to that variable in every possible preceding code path.

示例

🌐 Example

ts
// Compiled with --strictNullChecks
let x: number;
let y: number | null;
let z: number | undefined;
x; // Error, reference not preceded by assignment
y; // Error, reference not preceded by assignment
z; // Ok
x = 1;
y = null;
x; // Ok
y; // Ok

编译器通过执行_基于控制流的类型分析_来检查变量是否被确切地赋值。关于这一主题的更多细节请参阅后文。

🌐 The compiler checks that variables are definitely assigned by performing control flow based type analysis. See later for further details on this topic.

可选参数和属性

🌐 Optional parameters and properties

可选参数和属性会自动在它们的类型上添加 undefined,即使它们的类型注解中没有特别包括 undefined。例如,以下两种类型是相同的:

🌐 Optional parameters and properties automatically have undefined added to their types, even when their type annotations don’t specifically include undefined. For example, the following two types are identical:

ts
// Compiled with --strictNullChecks
type T1 = (x?: number) => string; // x has type number | undefined
type T2 = (x?: number | undefined) => string; // x has type number | undefined

非空和非未定义类型保护

🌐 Non-null and non-undefined type guards

如果对象或函数的类型包含 nullundefined,则属性访问或函数调用会产生编译时错误。然而,类型保护已扩展以支持非 null 和非 undefined 检查。

🌐 A property access or a function call produces a compile-time error if the object or function is of a type that includes null or undefined. However, type guards are extended to support non-null and non-undefined checks.

示例

🌐 Example

ts
// Compiled with --strictNullChecks
declare function f(x: number): string;
let x: number | null | undefined;
if (x) {
f(x); // Ok, type of x is number here
} else {
f(x); // Error, type of x is number? here
}
let a = x != null ? f(x) : ""; // Type of a is string
let b = x && f(x); // Type of b is string | 0 | null | undefined

非 null 和非 undefined 类型守卫可以使用 ==!====!== 运算符与 nullundefined 进行比较,例如 x != nullx === undefined。 对主体变量类型的影响准确反映了 JavaScript 的语义(例如,双等号运算符会检查两个值,无论指定的是哪一个,而三等号只检查指定的值)。

🌐 Non-null and non-undefined type guards may use the ==, !=, ===, or !== operator to compare to null or undefined, as in x != null or x === undefined. The effects on subject variable types accurately reflect JavaScript semantics (e.g. double-equals operators check for both values no matter which one is specified whereas triple-equals only checks for the specified value).

类型保护中的点名

🌐 Dotted names in type guards

类型保护以前只支持检查局部变量和参数。 类型保护现在支持检查“点状名称”,其由一个变量或参数名称后跟一个或多个属性访问组成。

🌐 Type guards previously only supported checking local variables and parameters. Type guards now support checking “dotted names” consisting of a variable or parameter name followed by one or more property accesses.

示例

🌐 Example

ts
interface Options {
location?: {
x?: number;
y?: number;
};
}
function foo(options?: Options) {
if (options && options.location && options.location.x) {
const x = options.location.x; // Type of x is number
}
}

点状名称的类型保护也适用于用户定义的类型保护函数以及 typeofinstanceof 操作符,并且不依赖于 strictNullChecks 编译器选项。

🌐 Type guards for dotted names also work with user defined type guard functions and the typeof and instanceof operators and do not depend on the strictNullChecks compiler option.

对点状名称的类型保护在对点状名称的任何部分进行赋值之后不起作用。例如,对 x.y.z 的类型保护在对 xx.yx.y.z 进行赋值之后不起作用。

🌐 A type guard for a dotted name has no effect following an assignment to any part of the dotted name. For example, a type guard for x.y.z will have no effect following an assignment to x, x.y, or x.y.z.

表达式运算符

🌐 Expression operators

表达式运算符允许操作数类型包括 null 和/或 undefined,但始终产生非 null 和非 undefined 类型的值。

🌐 Expression operators permit operand types to include null and/or undefined but always produce values of non-null and non-undefined types.

ts
// Compiled with --strictNullChecks
function sum(a: number | null, b: number | null) {
return a + b; // Produces value of type number
}

&& 运算符会根据左操作数类型中存在的情况,将 null 和/或 undefined 添加到右操作数的类型中,而 || 运算符会从结果联合类型中的左操作数类型中移除 nullundefined

🌐 The && operator adds null and/or undefined to the type of the right operand depending on which are present in the type of the left operand, and the || operator removes both null and undefined from the type of the left operand in the resulting union type.

ts
// Compiled with --strictNullChecks
interface Entity {
name: string;
}
let x: Entity | null;
let s = x && x.name; // s is of type string | null
let y = x || { name: "test" }; // y is of type Entity

类型扩展

🌐 Type widening

在严格空值检查模式下,nullundefined 类型不会被扩展为 any

🌐 The null and undefined types are not widened to any in strict null checking mode.

ts
let z = null; // Type of z is null

在常规类型检查模式下,z 的推断类型是 any,这是由于类型的扩展;但在严格空值检查模式下,z 的推断类型是 null(因此,如果没有类型注解,z 的唯一可能值是 null)。

🌐 In regular type checking mode the inferred type of z is any because of widening, but in strict null checking mode the inferred type of z is null (and therefore, absent a type annotation, null is the only possible value for z).

非空断言运算符

🌐 Non-null assertion operator

一个新的 ! 后缀表达式操作符可以用于断言其操作数在类型检查器无法确认的情况下非 null 且非 undefined。
具体而言,操作 x! 生成一个类型为 x 的值,其中排除了 nullundefined
类似于形式为 <T>xx as T 的类型断言,! 非空断言操作符在生成的 JavaScript 代码中会被直接移除。

🌐 A new ! post-fix expression operator may be used to assert that its operand is non-null and non-undefined in contexts where the type checker is unable to conclude that fact. Specifically, the operation x! produces a value of the type of x with null and undefined excluded. Similar to type assertions of the forms <T>x and x as T, the ! non-null assertion operator is simply removed in the emitted JavaScript code.

ts
// Compiled with --strictNullChecks
function validateEntity(e?: Entity) {
// Throw exception if e is null or invalid entity
}
function processEntity(e?: Entity) {
validateEntity(e);
let s = e!.name; // Assert that e is non-null and access name
}

兼容性

🌐 Compatibility

新功能的设计方式使其可以在严格空值检查模式和常规类型检查模式下使用。特别是,在常规类型检查模式下,nullundefined 类型会自动从联合类型中移除(因为它们是其他所有类型的子类型),而 ! 非空断言表达式操作符是允许的,但在常规类型检查模式下没有效果。因此,更新为使用可空和未定义感知类型的声明文件仍然可以在常规类型检查模式下使用,以保持向后兼容性。

🌐 The new features are designed such that they can be used in both strict null checking mode and regular type checking mode. In particular, the null and undefined types are automatically erased from union types in regular type checking mode (because they are subtypes of all other types), and the ! non-null assertion expression operator is permitted but has no effect in regular type checking mode. Thus, declaration files that are updated to use null- and undefined-aware types can still be used in regular type checking mode for backwards compatibility.

实际上,严格空值检查模式要求编译中的所有文件都具备空值和未定义感知能力。

🌐 In practical terms, strict null checking mode requires that all files in a compilation are null- and undefined-aware.

基于控制流的类型分析

🌐 Control flow based type analysis

TypeScript 2.0 对局部变量和参数实现了基于控制流的类型分析。以前,对类型保护进行的类型分析仅限于 if 语句和 ?: 条件表达式,并不包括赋值和诸如 returnbreak 语句之类的控制流构造的影响。在 TypeScript 2.0 中,类型检查器会分析语句和表达式中所有可能的控制流,以在任何给定位置为声明为联合类型的局部变量或参数产生最具体的类型(即 缩小后的类型)。

🌐 TypeScript 2.0 implements a control flow-based type analysis for local variables and parameters. Previously, the type analysis performed for type guards was limited to if statements and ?: conditional expressions and didn’t include effects of assignments and control flow constructs such as return and break statements. With TypeScript 2.0, the type checker analyses all possible flows of control in statements and expressions to produce the most specific type possible (the narrowed type) at any given location for a local variable or parameter that is declared to have a union type.

示例

🌐 Example

ts
function foo(x: string | number | boolean) {
if (typeof x === "string") {
x; // type of x is string here
x = 1;
x; // type of x is number here
}
x; // type of x is number | boolean here
}
function bar(x: string | number) {
if (typeof x === "number") {
return;
}
x; // type of x is string here
}

基于控制流的类型分析在 strictNullChecks 模式下尤其相关,因为可为空类型是使用联合类型表示的:

🌐 Control flow based type analysis is particularly relevant in strictNullChecks mode because nullable types are represented using union types:

ts
function test(x: string | null) {
if (x === null) {
return;
}
x; // type of x is string in remainder of function
}

此外,在strictNullChecks模式下,基于控制流的类型分析包括对不允许值为undefined的类型的局部变量进行_确定赋值分析_。

🌐 Furthermore, in strictNullChecks mode, control flow based type analysis includes definite assignment analysis for local variables of types that don’t permit the value undefined.

ts
function mumble(check: boolean) {
let x: number; // Type doesn't permit undefined
x; // Error, x is undefined
if (check) {
x = 1;
x; // Ok
}
x; // Error, x is possibly undefined
x = 2;
x; // Ok
}

带标签的联合类型

🌐 Tagged union types

TypeScript 2.0 实现了对标记(或判别)联合类型的支持。具体来说,TS 编译器现在支持基于判别属性测试的类型保护,从而缩小联合类型,并且进一步将该能力扩展到 switch 语句。

🌐 TypeScript 2.0 implements support for tagged (or discriminated) union types. Specifically, the TS compiler now support type guards that narrow union types based on tests of a discriminant property and furthermore extend that capability to switch statements.

示例

🌐 Example

ts
interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Circle {
kind: "circle";
radius: number;
}
type Shape = Square | Rectangle | Circle;
function area(s: Shape) {
// In the following switch statement, the type of s is narrowed in each case clause
// according to the value of the discriminant property, thus allowing the other properties
// of that variant to be accessed without a type assertion.
switch (s.kind) {
case "square":
return s.size * s.size;
case "rectangle":
return s.width * s.height;
case "circle":
return Math.PI * s.radius * s.radius;
}
}
function test1(s: Shape) {
if (s.kind === "square") {
s; // Square
} else {
s; // Rectangle | Circle
}
}
function test2(s: Shape) {
if (s.kind === "square" || s.kind === "rectangle") {
return;
}
s; // Circle
}

“判别属性类型保护”是一种形式为 x.p == vx.p === vx.p != vx.p !== v 的表达式,其中 pv 分别是属性和字符串字面量类型或字符串字面量类型联合的表达式。判别属性类型保护将 x 的类型缩小为 x 的那些构成类型,这些类型具有判别属性 p,且其值为 v 的可能值之一。

🌐 A discriminant property type guard is an expression of the form x.p == v, x.p === v, x.p != v, or x.p !== v, where p and v are a property and an expression of a string literal type or a union of string literal types. The discriminant property type guard narrows the type of x to those constituent types of x that have a discriminant property p with one of the possible values of v.

请注意,我们目前仅支持字符串字面量类型的判别属性。我们计划在以后添加对布尔和数字字面量类型的支持。

🌐 Note that we currently only support discriminant properties of string literal types. We intend to later add support for boolean and numeric literal types.

never 类型

🌐 The never type

TypeScript 2.0 引入了一种新的原始类型 nevernever 类型表示永远不会出现的值的类型。 具体来说,never 是永远不返回的函数的返回类型,而 never 是类型保护下永远不为真的变量的类型。

🌐 TypeScript 2.0 introduces a new primitive type never. The never type represents the type of values that never occur. Specifically, never is the return type for functions that never return and never is the type of variables under type guards that are never true.

never 类型具有以下特性:

🌐 The never type has the following characteristics:

  • never 是每种类型的子类型,并且可以被赋值给每种类型。
  • 没有类型是 never 的子类型或可赋值类型(除了 never 本身)。
  • 在没有返回类型注解的函数表达式或箭头函数中,如果函数没有 return 语句,或者仅有类型为 neverreturn 语句,并且函数的终点不可达(通过控制流分析确定),则该函数的推断返回类型为 never
  • 在具有显式 never 返回类型注解的函数中,所有 return 语句(如果有)必须具有类型为 never 的表达式,并且函数的终点不可到达。

因为 never 是每种类型的子类型,所以它总是会从联合类型中省略,并且在函数返回类型推断中会被忽略,只要还有其他类型被返回。

🌐 Because never is a subtype of every type, it is always omitted from union types and it is ignored in function return type inference as long as there are other types being returned.

返回 never 的函数示例:

🌐 Some examples of functions returning never:

ts
// Function returning never must have unreachable end point
function error(message: string): never {
throw new Error(message);
}
// Inferred return type is never
function fail() {
return error("Something failed");
}
// Function returning never must have unreachable end point
function infiniteLoop(): never {
while (true) {}
}

一些返回 never 的函数使用示例:

🌐 Some examples of use of functions returning never:

ts
// Inferred return type is number
function move1(direction: "up" | "down") {
switch (direction) {
case "up":
return 1;
case "down":
return -1;
}
return error("Should never get here");
}
// Inferred return type is number
function move2(direction: "up" | "down") {
return direction === "up"
? 1
: direction === "down"
? -1
: error("Should never get here");
}
// Inferred return type is T
function check<T>(x: T | undefined) {
return x || error("Undefined value");
}

因为 never 可以赋值给每种类型,当需要一个回调返回更具体类型时,可以使用返回 never 的函数:

🌐 Because never is assignable to every type, a function returning never can be used when a callback returning a more specific type is required:

ts
function test(cb: () => string) {
let s = cb();
return s;
}
test(() => "hello");
test(() => fail());
test(() => {
throw new Error();
});

只读属性和索引签名

🌐 Read-only properties and index signatures

现在可以使用 readonly 修饰符声明属性或索引签名。

🌐 A property or index signature can now be declared with the readonly modifier.

只读属性可以具有初始化函数,并且可以在同一类声明中的构造函数中赋值,但除此之外,不允许对只读属性进行赋值。

🌐 Read-only properties may have initializers and may be assigned to in constructors within the same class declaration, but otherwise assignments to read-only properties are disallowed.

此外,在几种情况下实体是_隐式_只读的:

🌐 In addition, entities are implicitly read-only in several situations:

  • 使用 get 存取器声明且没有 set 存取器的属性被视为只读。
  • 在枚举对象类型中,枚举成员被视为只读属性。
  • 在模块对象的类型中,导出的 const 变量被视为只读属性。
  • import 语句中声明的实体被视为只读。
  • 通过 ES2015 命名空间导入访问的实体被视为只读(例如,当 foo 被声明为 import * as foo from "foo" 时,foo.x 是只读的)。
示例

🌐 Example

ts
interface Point {
readonly x: number;
readonly y: number;
}
var p1: Point = { x: 10, y: 20 };
p1.x = 5; // Error, p1.x is read-only
var p2 = { x: 1, y: 1 };
var p3: Point = p2; // Ok, read-only alias for p2
p3.x = 5; // Error, p3.x is read-only
p2.x = 5; // Ok, but also changes p3.x because of aliasing
ts
class Foo {
readonly a = 1;
readonly b: string;
constructor() {
this.b = "hello"; // Assignment permitted in constructor
}
}
ts
let a: Array<number> = [0, 1, 2, 3, 4];
let b: ReadonlyArray<number> = a;
b[5] = 5; // Error, elements are read-only
b.push(5); // Error, no push method (because it mutates array)
b.length = 3; // Error, length is read-only
a = b; // Error, mutating methods are missing

为函数指定 this 的类型

🌐 Specifying the type of this for functions

在继续指定类或接口中 this 的类型之后,函数和方法现在可以声明它们所期望的 this 类型。

🌐 Following up on specifying the type of this in a class or an interface, functions and methods can now declare the type of this they expect.

默认情况下,函数内部 this 的类型是 any。从 TypeScript 2.0 开始,你可以提供一个显式的 this 参数。this 参数是假参数,位于函数参数列表的最前面:

🌐 By default the type of this inside a function is any. Starting with TypeScript 2.0, you can provide an explicit this parameter. this parameters are fake parameters that come first in the parameter list of a function:

ts
function f(this: void) {
// make sure `this` is unusable in this standalone function
}

回调中的 this 参数

🌐 this parameters in callbacks

库也可以使用 this 参数来声明回调的调用方式。

🌐 Libraries can also use this parameters to declare how callbacks will be invoked.

示例

🌐 Example

ts
interface UIElement {
addClickListener(onclick: (this: void, e: Event) => void): void;
}

this: void 意味着 addClickListener 期望 onclick 是一个不需要 this 类型的函数。

现在如果你在调用代码上标注 this

🌐 Now if you annotate calling code with this:

ts
class Handler {
info: string;
onClickBad(this: Handler, e: Event) {
// oops, used this here. using this callback would crash at runtime
this.info = e.message;
}
}
let h = new Handler();
uiElement.addClickListener(h.onClickBad); // error!

--noImplicitThis

在 TypeScript 2.0 中还新增了一个标志,用于标记所有在没有显式类型注解的函数中使用的 this

🌐 A new flag is also added in TypeScript 2.0 to flag all uses of this in functions without an explicit type annotation.

tsconfig.json中的全局支持

🌐 Glob support in tsconfig.json

Glob 支持来了!!Glob 支持一直是最受欢迎的功能请求之一

🌐 Glob support is here!! Glob support has been one of the most requested features.

类似通配符的文件模式由两个属性 includeexclude 支持。

🌐 Glob-like file patterns are supported by two properties include and exclude.

示例

🌐 Example

{
"": "commonjs",
"": true,
"": true,
"": "../../built/local/tsc.js",
"": true
},
"": ["src/**/*"],
"": ["node_modules", "**/*.spec.ts"]
}

支持的全局通配符为:

🌐 The supported glob wildcards are:

  • * 匹配零个或多个字符(不包括目录分隔符)
  • ? 匹配任意一个字符(不包括目录分隔符)
  • **/ 递归匹配任何子目录

如果通配符模式的某个片段只包含 *.*,那么只会包含具有支持扩展名的文件(例如默认情况下包括 .ts.tsx.d.ts,如果 allowJs 设置为 true,则还包括 .js.jsx)。

🌐 If a segment of a glob pattern includes only * or .*, then only files with supported extensions are included (e.g. .ts, .tsx, and .d.ts by default with .js and .jsx if allowJs is set to true).

如果 filesinclude 均未指定,编译器默认包含包含目录及其子目录中的所有 TypeScript(.ts.d.ts.tsx)文件,但排除通过 exclude 属性排除的文件。如果 allowJs 设置为 true,JS 文件(.js.jsx)也会被包含。

🌐 If the files and include are both left unspecified, the compiler defaults to including all TypeScript (.ts, .d.ts and .tsx) files in the containing directory and subdirectories except those excluded using the exclude property. JS files (.js and .jsx) are also included if allowJs is set to true.

如果指定了 filesinclude 属性,编译器将改为包含这两个属性所包含文件的联合集合。使用 outDir 编译器选项指定的目录中的文件总是被排除,除非通过 files 属性显式包含(即使指定了 exclude 属性)。

🌐 If the files or include properties are specified, the compiler will instead include the union of the files included by those two properties. Files in the directory specified using the outDir compiler option are always excluded unless explicitly included via the files property (even when the exclude property is specified).

使用 include 包含的文件可以使用 exclude 属性进行过滤。然而,使用 files 属性显式包含的文件无论 exclude 如何总是会被包含。未指定时,exclude 属性默认排除 node_modulesbower_componentsjspm_packages 目录。

🌐 Files included using include can be filtered using the exclude property. However, files included explicitly using the files property are always included regardless of exclude. The exclude property defaults to excluding the node_modules, bower_components, and jspm_packages directories when not specified.

模块解析增强:BaseUrl、路径映射、rootDirs 和跟踪

🌐 Module resolution enhancements: BaseUrl, Path mapping, rootDirs and tracing

TypeScript 2.0 提供了一组额外的模块解析选项,用于 告知 编译器在哪里可以找到给定模块的声明。

🌐 TypeScript 2.0 provides a set of additional module resolution knops to inform the compiler where to find declarations for a given module.

有关更多详情,请参阅 模块解析 文档。

🌐 See Module Resolution documentation for more details.

基本 URL

🌐 Base URL

在使用 AMD 模块加载器的应用中,使用 baseUrl 是一种常见做法,模块在运行时会被“部署”到一个文件夹中。所有使用裸符号名称的模块导入都被假定为相对于 baseUrl

🌐 Using a baseUrl is a common practice in applications using AMD module loaders where modules are “deployed” to a single folder at run-time. All module imports with bare specifier names are assumed to be relative to the baseUrl.

示例

🌐 Example

{
"": "./modules"
}
}

现在对 "moduleA" 的导入将会在 ./modules/moduleA 中查找

🌐 Now imports to "moduleA" would be looked up in ./modules/moduleA

ts
import A from "moduleA";

路径映射

🌐 Path mapping

有时模块并不直接位于 baseUrl 下。加载器使用映射配置在运行时将模块名称映射到文件,详情请参阅 RequireJs 文档SystemJS 文档

🌐 Sometimes modules are not directly located under baseUrl. Loaders use a mapping configuration to map module names to files at run-time, see RequireJs documentation and SystemJS documentation.

TypeScript 编译器支持在 tsconfig.json 文件中使用 paths 属性来声明此类映射。

🌐 The TypeScript compiler supports the declaration of such mappings using paths property in tsconfig.json files.

示例

🌐 Example

例如,对模块 "jquery" 的导入将在运行时被翻译为 "node_modules/jquery/dist/jquery.slim.min.js"

🌐 For instance, an import to a module "jquery" would be translated at runtime to "node_modules/jquery/dist/jquery.slim.min.js".

{
"": "./node_modules",
"": {
"jquery": ["jquery/dist/jquery.slim.min"]
}
}

使用 paths 还可以实现更复杂的映射,包括多个备用位置。考虑一个项目配置,其中只有部分模块在一个位置可用,其余模块在另一个位置。

🌐 Using paths also allows for more sophisticated mappings including multiple fall back locations. Consider a project configuration where only some modules are available in one location, and the rest are in another.

使用 rootDirs 的虚拟目录

🌐 Virtual Directories with rootDirs

使用 ‘rootDirs’,你可以告知编译器组成这个“虚拟”目录的 根目录;从而编译器可以在这些“虚拟”目录内解析相对模块导入,就好像它们被合并在一个目录中一样。

🌐 Using ‘rootDirs’, you can inform the compiler of the roots making up this “virtual” directory; and thus the compiler can resolve relative modules imports within these “virtual” directories as if they were merged together in one directory.

示例

🌐 Example

给定此项目结构:

🌐 Given this project structure:

src └── views └── view1.ts (imports './template1') └── view2.ts generated └── templates └── views └── template1.ts (imports './view2')

构建步骤会将 /src/views/generated/templates/views 中的文件复制到输出中的同一目录。运行时,视图可以期望其模板存在于它旁边,因此应该使用相对名称如 "./template" 来导入模板。

🌐 A build step will copy the files in /src/views and /generated/templates/views to the same directory in the output. At run-time, a view can expect its template to exist next to it, and thus should import it using a relative name as "./template".

rootDirs 指定一个 根目录 列表,其内容预计将在运行时合并。因此,按照我们的示例,tsconfig.json 文件应如下所示:

{
"": ["src/views", "generated/templates/views"]
}
}

跟踪模块解析

🌐 Tracing module resolution

traceResolution 提供了一种方便的方式来理解编译器如何解析模块。

shell
tsc --traceResolution

环境模块声明简写

🌐 Shorthand ambient module declarations

如果你不想在使用新模块前花时间编写声明,现在只需使用简写声明即可快速上手。

🌐 If you don’t want to take the time to write out declarations before using a new module, you can now just use a shorthand declaration to get started quickly.

declarations.d.ts
ts
declare module "hot-new-module";

所有从简写模块导入的函数都将具有 any 类型。

🌐 All imports from a shorthand module will have the any type.

ts
import x, { y } from "hot-new-module";
x(y);

模块名称中的通配符

🌐 Wildcard character in module names

使用模块加载器扩展(例如 AMDSystemJS)导入非代码资源以前并不容易;以前必须为每个资源定义一个全局模块声明。

🌐 Importing non-code resources using module loaders extension (e.g. AMD or SystemJS) has not been easy before; previously an ambient module declaration had to be defined for each resource.

TypeScript 2.0 支持使用通配符字符(*)来声明一组模块名称;这样,扩展只需声明一次,而不需要为每个资源单独声明。

🌐 TypeScript 2.0 supports the use of the wildcard character (*) to declare a “family” of module names; this way, a declaration is only required once for an extension, and not for every resource.

示例

🌐 Example

ts
declare module "*!text" {
const content: string;
export default content;
}
// Some do it the other way around.
declare module "json!*" {
const value: any;
export default value;
}

现在你可以导入匹配 "*!text""json!*" 的内容。

🌐 Now you can import things that match "*!text" or "json!*".

ts
import fileContent from "./xyz.txt!text";
import data from "json!http://example.com/data.json";
console.log(data, fileContent);

在从未类型化的代码库迁移时,通配符模块名会更有用。结合简写的全局模块声明,一组模块可以很容易地声明为 any

🌐 Wildcard module names can be even more useful when migrating from an un-typed code base. Combined with Shorthand ambient module declarations, a set of modules can be easily declared as any.

示例

🌐 Example

ts
declare module "myLibrary/*";

myLibrary 下的任何模块的所有导入,编译器都会认为其类型为 any;因此,将关闭对这些模块的形状或类型的任何检查。

🌐 All imports to any module under myLibrary would be considered to have the type any by the compiler; thus, shutting down any checking on the shapes or types of these modules.

ts
import { readFile } from "myLibrary/fileSystem/readFile`;
readFile(); // readFile is 'any'

支持 UMD 模块定义

🌐 Support for UMD module definitions

有些库被设计成可以在多种模块加载器中使用,或者在没有模块加载器的情况下使用(全局变量)。这些被称为 UMDIsomorphic 模块。这些库可以通过导入或全局变量访问。

🌐 Some libraries are designed to be used in many module loaders, or with no module loading (global variables). These are known as UMD or Isomorphic modules. These libraries can be accessed through either an import or a global variable.

例如:

🌐 For example:

math-lib.d.ts
ts
export const isPrime(x: number): boolean;
export as namespace mathLib;

该库随后可在模块内导入使用:

🌐 The library can then be used as an import within modules:

ts
import { isPrime } from "math-lib";
isPrime(2);
mathLib.isPrime(2); // ERROR: can't use the global definition from inside a module

它也可以作为全局变量使用,但仅限于脚本内部。(脚本是指没有导入或导出的文件。)

🌐 It can also be used as a global variable, but only inside of a script. (A script is a file with no imports or exports.)

ts
mathLib.isPrime(2);

可选类属性

🌐 Optional class properties

现在可以在类中声明可选属性和方法,类似于接口中已经允许的操作。

🌐 Optional properties and methods can now be declared in classes, similar to what is already permitted in interfaces.

示例

🌐 Example

ts
class Bar {
a: number;
b?: number;
f() {
return 1;
}
g?(): number; // Body of optional method can be omitted
h?() {
return 2;
}
}

当以 strictNullChecks 模式编译时,可选属性和方法会自动在其类型中包含 undefined。因此,上面的 b 属性的类型是 number | undefined,上面 g 方法的类型是 (() => number) | undefined。 类型保护可以用来去掉类型中的 undefined 部分:

🌐 When compiled in strictNullChecks mode, optional properties and methods automatically have undefined included in their type. Thus, the b property above is of type number | undefined and the g method above is of type (() => number) | undefined. Type guards can be used to strip away the undefined part of the type:

ts
function test(x: Bar) {
x.a; // number
x.b; // number | undefined
x.f; // () => number
x.g; // (() => number) | undefined
let f1 = x.f(); // number
let g1 = x.g && x.g(); // number | undefined
let g2 = x.g ? x.g() : 0; // number
}

私有和受保护的构造函数

🌐 Private and Protected Constructors

类构造函数可以标记为 privateprotected。 具有私有构造函数的类无法在类体外实例化,也不能被继承。 具有受保护构造函数的类无法在类体外实例化,但可以被继承。

🌐 A class constructor may be marked private or protected. A class with a private constructor cannot be instantiated outside the class body, and cannot be extended. A class with a protected constructor cannot be instantiated outside the class body, but can be extended.

示例

🌐 Example

ts
class Singleton {
private static instance: Singleton;
private constructor() {}
static getInstance() {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
}
let e = new Singleton(); // Error: constructor of 'Singleton' is private.
let v = Singleton.getInstance();

抽象属性和访问器

🌐 Abstract properties and accessors

抽象类可以声明抽象属性和/或访问器。任何子类都需要声明这些抽象属性或被标记为抽象。抽象属性不能有初始化器。抽象访问器不能有方法体。

🌐 An abstract class can declare abstract properties and/or accessors. Any sub class will need to declare the abstract properties or be marked as abstract. Abstract properties cannot have an initializer. Abstract accessors cannot have bodies.

示例

🌐 Example

ts
abstract class Base {
abstract name: string;
abstract get value();
abstract set value(v: number);
}
class Derived extends Base {
name = "derived";
value = 1;
}

隐式索引签名

🌐 Implicit index signatures

如果对象字面量中的所有已知属性都可以赋值给索引签名,那么对象字面量类型现在可以赋值给具有索引签名的类型。这使得可以将使用对象字面量初始化的变量作为参数传递给期望接收映射或字典的函数:

🌐 An object literal type is now assignable to a type with an index signature if all known properties in the object literal are assignable to that index signature. This makes it possible to pass a variable that was initialized with an object literal as parameter to a function that expects a map or dictionary:

ts
function httpService(path: string, headers: { [x: string]: string }) {}
const headers = {
"Content-Type": "application/x-www-form-urlencoded",
};
httpService("", { "Content-Type": "application/x-www-form-urlencoded" }); // Ok
httpService("", headers); // Now ok, previously wasn't

包括带有 --lib 的内置类型声明

🌐 Including built-in type declarations with --lib

在 ES6/ES2015 时代,内置 API 声明仅限于 target: ES6。 输入 lib;使用 lib 你可以指定一系列内置 API 声明组,选择包含到你的项目中。 例如,如果你预计运行环境支持 MapSetPromise(例如现在的大多数现代浏览器),只需包含 --lib es2015.collection,es2015.promise。 同样,你也可以排除不想包含在项目中的声明,例如,如果你在使用 --lib es5,es6 的 Node 项目中工作,可以排除 DOM。

🌐 Getting to ES6/ES2015 built-in API declarations were only limited to target: ES6. Enter lib; with lib you can specify a list of built-in API declaration groups that you can choose to include in your project. For instance, if you expect your runtime to have support for Map, Set and Promise (e.g. most evergreen browsers today), just include --lib es2015.collection,es2015.promise. Similarly you can exclude declarations you do not want to include in your project, e.g. DOM if you are working on a node project using --lib es5,es6.

以下是可用 API 组列表:

🌐 Here is a list of available API groups:

  • dom
  • webworker
  • es5
  • es6 / es2015
  • es2015.core
  • es2015.collection
  • es2015.iterable
  • es2015.promise
  • es2015.proxy
  • es2015.reflect
  • es2015.generator
  • es2015.symbol
  • es2015.symbol.wellknown
  • es2016
  • es2016.array.include
  • es2017
  • es2017.object
  • es2017.sharedmemory
  • scripthost
示例

🌐 Example

bash
tsc --target es5 --lib es5,es2015.promise
"compilerOptions": {
"": ["es5", "es2015.promise"]
}

--noUnusedParameters--noUnusedLocals 标记未使用的声明

🌐 Flag unused declarations with --noUnusedParameters and --noUnusedLocals

TypeScript 2.0 有两个新标志可以帮助你保持代码库的整洁。 noUnusedParameters 会标记任何未使用的函数或方法参数错误。 noUnusedLocals 会标记任何未使用的本地(未导出)声明,如变量、函数、类、导入等。 此外,在 noUnusedLocals 下,类的未使用私有成员也会被标记为错误。

🌐 TypeScript 2.0 has two new flags to help you maintain a clean code base. noUnusedParameters flags any unused function or method parameters errors. noUnusedLocals flags any unused local (un-exported) declaration like variables, functions, classes, imports, etc… Also, unused private members of a class would be flagged as errors under noUnusedLocals.

示例

🌐 Example

ts
import B, { readFile } from "./b";
// ^ Error: `B` declared but never used
readFile();
export function write(message: string, args: string[]) {
// ^^^^ Error: 'arg' declared but never used.
console.log(message);
}

_ 开头命名的参数声明不受未使用参数检查的约束。例如:

🌐 Parameters declaration with names starting with _ are exempt from the unused parameter checking. e.g.:

ts
function returnNull(_a) {
// OK
return null;
}

模块标识符允许 .js 扩展

🌐 Module identifiers allow for .js extension

在 TypeScript 2.0 之前,模块标识符总是被假定为没有扩展名;例如,给定一个名为 import d from "./moduleA.js" 的导入,编译器会在 ./moduleA.js.ts./moduleA.js.d.ts 中查找 "moduleA.js" 的定义。这使得使用像 SystemJS 这样的打包/加载工具变得困难,因为它们期望模块标识符为 URI。

🌐 Before TypeScript 2.0, a module identifier was always assumed to be extension-less; for instance, given an import as import d from "./moduleA.js", the compiler looked up the definition of "moduleA.js" in ./moduleA.js.ts or ./moduleA.js.d.ts. This made it hard to use bundling/loading tools like SystemJS that expect URI’s in their module identifier.

在 TypeScript 2.0 中,编译器会在 ./moduleA.ts./moduleA.d.t 中查找 "moduleA.js" 的定义。

🌐 With TypeScript 2.0, the compiler will look up definition of "moduleA.js" in ./moduleA.ts or ./moduleA.d.t.

支持在 ‘module: es6’ 下使用 ‘target : es5’

🌐 Support ‘target : es5’ with ‘module: es6’

先前被标记为无效标志组合的 target: es5 和 ‘module: es6’ 现在已被支持。这将有助于使用基于 ES2015 的 tree shaker,例如 rollup

🌐 Previously flagged as an invalid flag combination, target: es5 and ‘module: es6’ is now supported. This should facilitate using ES2015-based tree shakers like rollup.

函数形参和实参列表中的尾随逗号

🌐 Trailing commas in function parameter and argument lists

函数参数和参数列表中的尾随逗号现在被允许。这是一个Stage-3 ECMAScript 提案的实现,可以编译为有效的 ES3/ES5/ES6。

🌐 Trailing comma in function parameter and argument lists are now allowed. This is an implementation for a Stage-3 ECMAScript proposal that emits down to valid ES3/ES5/ES6.

示例

🌐 Example

ts
function foo(
bar: Bar,
baz: Baz // trailing commas are OK in parameter lists
) {
// Implementation...
}
foo(
bar,
baz // and in argument lists
);

新的 --skipLibCheck

🌐 New --skipLibCheck

TypeScript 2.0 添加了一个新的 skipLibCheck 编译器选项,该选项会跳过对声明文件(扩展名为 .d.ts 的文件)的类型检查。 当程序包含大型声明文件时,编译器会花费大量时间检查那些已知不包含错误的声明,通过跳过声明文件类型检查,可以显著缩短编译时间。

🌐 TypeScript 2.0 adds a new skipLibCheck compiler option that causes type checking of declaration files (files with extension .d.ts) to be skipped. When a program includes large declaration files, the compiler spends a lot of time type checking declarations that are already known to not contain errors, and compile times may be significantly shortened by skipping declaration file type checks.

由于一个文件中的声明可能会影响其他文件的类型检查,因此当指定 skipLibCheck 时,某些错误可能无法被检测到。例如,如果一个非声明文件扩展了在声明文件中声明的类型,可能会产生错误,这些错误只有在检查声明文件时才会被报告。然而,在实际情况中,这种情况很少发生。

🌐 Since declarations in one file can affect type checking in other files, some errors may not be detected when skipLibCheck is specified. For example, if a non-declaration file augments a type declared in a declaration file, errors may result that are only reported when the declaration file is checked. However, in practice such situations are rare.

允许跨声明使用重复的标识符

🌐 Allow duplicate identifiers across declarations

这是导致重复定义错误的一个常见原因。多个声明文件在接口上定义相同的成员。

🌐 This has been one common source of duplicate definition errors. Multiple declaration files defining the same members on interfaces.

TypeScript 2.0 放宽了这一限制,允许在不同代码块中使用重复的标识符,只要它们的类型完全相同。

🌐 TypeScript 2.0 relaxes this constraint and allows duplicate identifiers across blocks, as long as they have identical types.

在同一个块中仍然不允许重复定义。

🌐 Within the same block duplicate definitions are still disallowed.

示例

🌐 Example

ts
interface Error {
stack?: string;
}
interface Error {
code?: string;
path?: string;
stack?: string; // OK
}

新的 --declarationDir

🌐 New --declarationDir

[declarationDir](/tsconfig#declarationDir) 允许在与 JavaScript 文件不同的位置生成声明文件。