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 切换到新的严格空值检查模式。

¥strictNullChecks switches to a new strict null checking mode.

在严格空值检查模式下,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,则属性访问或函数调用会产生编译时错误。但是,类型保护已扩展以支持非空和非未定义检查。

¥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

非空和非未定义的类型保护可以使用 ==!====!== 运算符与 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.

对点分名称的任何部分进行赋值后,其类型保护均无效。例如,在对 xx.yx.y.z 赋值后,x.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,但始终会生成非空且非未定义类型的值。

¥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(因此,如果没有类型注解,nullz 的唯一可能值)。

¥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

新的 ! 后缀表达式运算符可用于在类型检查器无法断言其操作数非空且非未定义的情况下断言该操作数。具体来说,操作 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 类型会自动从联合类型中删除(因为它们是所有其他类型的子类型),并且 ! 非空断言表达式运算符在常规类型检查模式下允许使用,但不起作用。因此,更新为使用可识别 null 和 undefined 类型的声明文件仍可在常规类型检查模式下使用,以实现向后兼容性。

¥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 is a subtype of and assignable to every type.

  • 没有任何类型是 never 的子类型或可赋值给 nevernever 本身除外)。

    ¥No type is a subtype of or assignable to never (except never itself).

  • 在没有返回类型注释的函数表达式或箭头函数中,如果函数没有 return 语句,或者只有带有 never 类型表达式的 return 语句,并且函数的终点不可到达(由控制流分析确定),则该函数的推断返回类型为 never

    ¥In a function expression or arrow function with no return type annotation, if the function has no return statements, or only return statements with expressions of type never, and if the end point of the function is not reachable (as determined by control flow analysis), the inferred return type for the function is never.

  • 在具有显式 never 返回类型注释的函数中,所有 return 语句(如果有)必须包含 never 类型的表达式,并且函数的终点必须是不可访问的。

    ¥In a function with an explicit never return type annotation, all return statements (if any) must have expressions of type never and the end point of the function must not be reachable.

由于 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 访问器的属性将被视为只读。

    ¥A property declared with a get accessor and no set accessor is considered read-only.

  • 在枚举对象类型中,枚举成员被视为只读属性。

    ¥In the type of an enum object, enum members are considered read-only properties.

  • 在模块对象类型中,导出的 const 变量被视为只读属性。

    ¥In the type of a module object, exported const variables are considered read-only properties.

  • import 语句中声明的实体被视为只读。

    ¥An entity declared in an import statement is considered read-only.

  • 通过 ES2015 命名空间导入访问的实体被视为只读(例如,当 foo 声明为 import * as foo from "foo" 时,foo.x 为只读)。

    ¥An entity accessed through an ES2015 namespace import is considered read-only (e.g. foo.x is read-only when foo is declared as import * as foo from "foo").

示例

¥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: void means that addClickListener expects onclick to be a function that does not require a this type.

现在,如果你使用 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 支持

¥Glob support in tsconfig.json

全局变量支持现已推出!!最受期待的功能之一 已支持 Glob。

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

类似 Glob 的文件模式由两个属性 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:

  • * 匹配零个或多个字符(不包括目录分隔符)

    ¥* matches zero or more characters (excluding directory separators)

  • ? 匹配任何一个字符(不包括目录分隔符)

    ¥? matches any one character (excluding directory separators)

  • **/ 递归匹配任何子目录。

    ¥**/ recursively matches any subdirectory

如果 glob 模式的某个部分仅包含 *.*,则仅包含支持扩展名的文件(例如,默认情况下包含 .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 属性进行过滤。但是,无论 exclude 是什么,明确使用 files 属性包含的文件都会被包含。如果未指定,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 是一种常见做法,在应用中,模块在运行时被 “deployed” 到单个文件夹。所有带有裸说明符名称的模块导入都假定为相对于 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’,你可以告知编译器构成此 “virtual” 目录的根目录;因此,编译器可以解析这些 “virtual” 目录中的相对模块导入,就像它们合并到一个目录中一样。

¥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 文件应该如下所示:

¥rootDirs specify a list of roots whose contents are expected to merge at run-time. So following our example, the tsconfig.json file should look like:

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

跟踪模块解析

¥Tracing module resolution

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

¥traceResolution offers a handy way to understand how modules have been resolved by the compiler.

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 支持使用通配符 (*) 来声明模块名称的 “family”;这样,扩展只需要声明一次,而不是每个资源都需要声明一次。

¥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

一些库被设计为可在多个模块加载器中使用,或者无需模块加载(全局变量)。这些被称为 UMD同构 模块。这些库可以通过导入或全局变量访问。

¥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: 支持 ‘目标:es5’es6’

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

之前标记为无效的标志组合:target: es5 和 ‘module:es6’ 现已支持。这应该有助于使用基于 ES2015 的摇树优化器 (例如 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

现在允许在函数参数和实参列表中使用尾随逗号。这是一个 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 允许在与 JavaScript 文件不同的位置生成声明文件。

¥declarationDir allows for generating declaration files in a different location than JavaScript files.