Skip to content
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待

TypeScript 2.0

识别 null 和 undefined 的类型

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

类型检查器以前认为 nullundefined 可以赋值给任何类型。 实际上,nullundefined每种类型的有效值,并且不可能特别排除它们(因此不可能检测到它们的错误使用)。

--strictNullChecks

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

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

示例
ts
// 使用 --strictNullChecks 编译
let x: number;
let y: number | undefined;
let z: number | null | undefined;
x = 1; // 可以
y = 1; // 可以
z = 1; // 可以
x = undefined; // 错误
y = undefined; // 可以
z = undefined; // 可以
x = null; // 错误
y = null; // 错误
z = null; // 可以
x = y; // 错误
x = z; // 错误
y = x; // 可以
y = z; // 错误
z = x; // 可以
z = y; // 可以

使用前赋值检查

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

示例
ts
// 使用 --strictNullChecks 编译
let x: number;
let y: number | null;
let z: number | undefined;
x; // 错误,引用前没有赋值
y; // 错误,引用前没有赋值
z; // 可以
x = 1;
y = null;
x; // 可以
y; // 可以

编译器通过执行基于控制流的类型分析来检查变量是否被明确赋值。关于此主题的更多详细信息,请参见下文。

可选参数和属性

可选参数和属性会自动将 undefined 添加到它们的类型中,即使它们的类型注解没有明确包含 undefined。 例如,以下两种类型是相同的:

ts
// 使用 --strictNullChecks 编译
type T1 = (x?: number) => string; // x 的类型是 number | undefined
type T2 = (x?: number | undefined) => string; // x 的类型是 number | undefined

非空和非 undefined 类型守卫

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

示例
ts
// 使用 --strictNullChecks 编译
declare function f(x: number): string;
let x: number | null | undefined;
if (x) {
  f(x); // 可以,这里 x 的类型是 number
} else {
  f(x); // 错误,这里 x 的类型是 number? 
}
let a = x != null ? f(x) : ""; // a 的类型是 string
let b = x && f(x); // b 的类型是 string | 0 | null | undefined

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

类型守卫中的点名称

类型守卫以前仅支持检查局部变量和参数。 现在类型守卫支持检查由变量或参数名称后跟一个或多个属性访问组成的“点名称”。

示例
ts
interface Options {
  location?: {
    x?: number;
    y?: number;
  };
}

function foo(options?: Options) {
  if (options && options.location && options.location.x) {
    const x = options.location.x; // x 的类型是 number
  }
}

点名称的类型守卫也适用于用户定义的类型守卫函数以及 typeofinstanceof 运算符,并且不依赖于 strictNullChecks 编译器选项。

点名称的类型守卫在赋值给点名称的任何部分后无效。 例如,对 x.y.z 的类型守卫在对 xx.yx.y.z 赋值后无效。

表达式运算符

表达式运算符允许操作数类型包含 null 和/或 undefined,但总是产生非空和非 undefined 类型的值。

ts
// 使用 --strictNullChecks 编译
function sum(a: number | null, b: number | null) {
  return a + b; // 产生类型 number 的值
}

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

ts
// 使用 --strictNullChecks 编译
interface Entity {
  name: string;
}
let x: Entity | null;
let s = x && x.name; // s 的类型是 string | null
let y = x || { name: "test" }; // y 的类型是 Entity

类型拓宽

在严格空值检查模式下,nullundefined 类型不会拓宽为 any

ts
let z = null; // z 的类型是 null

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

非空断言运算符

一个新的 ! 后缀表达式运算符可用于断言其操作数在类型检查器无法得出该结论的上下文中是非空和非 undefined 的。 具体来说,操作 x! 产生一个类型为 x 的值,其中排除了 nullundefined。 类似于 <T>xx as T 形式的类型断言,! 非空断言运算符在输出的 JavaScript 代码中只是被移除。

ts
// 使用 --strictNullChecks 编译
function validateEntity(e?: Entity) {
  // 如果 e 是 null 或无效实体,则抛出异常
}

function processEntity(e?: Entity) {
  validateEntity(e);
  let s = e!.name; // 断言 e 非空并访问 name
}

兼容性

新特性的设计使得它们既可以在严格空值检查模式下使用,也可以在常规类型检查模式下使用。 特别是,在常规类型检查模式下,nullundefined 类型会自动从联合类型中擦除(因为它们是所有其他类型的子类型),并且 ! 非空断言表达式运算符是允许的,但在常规类型检查模式下无效。因此,更新为使用识别 null 和 undefined 的类型的声明文件仍然可以在常规类型检查模式下用于向后兼容。

实际上,严格空值检查模式要求编译中的所有文件都是识别 null 和 undefined 的。

基于控制流的类型分析

TypeScript 2.0 为局部变量和参数实现了基于控制流的类型分析。 以前,为类型守卫执行的类型分析仅限于 if 语句和 ?: 条件表达式,并且不包括赋值和 returnbreak 等控制流结构的影响。 使用 TypeScript 2.0,类型检查器分析语句和表达式中所有可能的控制流,以便为声明为具有联合类型的局部变量或参数在给定位置产生尽可能具体的类型(收窄类型)。

示例
ts
function foo(x: string | number | boolean) {
  if (typeof x === "string") {
    x; // 这里 x 的类型是 string
    x = 1;
    x; // 这里 x 的类型是 number
  }
  x; // 这里 x 的类型是 number | boolean
}

function bar(x: string | number) {
  if (typeof x === "number") {
    return;
  }
  x; // 这里 x 的类型是 string
}

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

ts
function test(x: string | null) {
  if (x === null) {
    return;
  }
  x; // 在函数剩余部分中 x 的类型是 string
}

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

ts
function mumble(check: boolean) {
  let x: number; // 类型不允许 undefined
  x; // 错误,x 是 undefined
  if (check) {
    x = 1;
    x; // 可以
  }
  x; // 错误,x 可能为 undefined
  x = 2;
  x; // 可以
}

带标签的联合类型

TypeScript 2.0 实现了对带标签(或可辨识)联合类型的支持。 具体来说,TS 编译器现在支持基于对可辨识属性的测试来收窄联合类型的类型守卫,并将该能力扩展到 switch 语句。

示例
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) {
  // 在下面的 switch 语句中,根据可辨识属性的值,在每个 case 子句中收窄 s 的类型,
  // 从而无需类型断言即可访问该变体的其他属性。
  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 的可能值之一。

请注意,我们目前仅支持字符串字面量类型的可辨识属性。 我们打算稍后添加对布尔和数字字面量类型的支持。

never 类型

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

never 类型具有以下特征:

  • never 是所有类型的子类型,并且可以赋值给任何类型。
  • 没有类型是 never 的子类型或可以赋值给 never(除了 never 本身)。
  • 在没有返回类型注解的函数表达式或箭头函数中,如果函数没有 return 语句,或者只有返回类型为 never 的表达式,并且函数的结束点不可达(由控制流分析确定),则推断出的函数返回类型是 never
  • 在具有显式 never 返回类型注解的函数中,所有 return 语句(如果有)必须具有类型为 never 的表达式,并且函数的结束点必须不可达。

由于 never 是所有类型的子类型,因此它总是从联合类型中省略,并且只要返回其他类型,在函数返回类型推断中就会被忽略。

一些返回 never 的函数示例:

ts
// 返回 never 的函数必须有不可达的结束点
function error(message: string): never {
  throw new Error(message);
}

// 推断的返回类型是 never
function fail() {
  return error("Something failed");
}

// 返回 never 的函数必须有不可达的结束点
function infiniteLoop(): never {
  while (true) {}
}

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

ts
// 推断的返回类型是 number
function move1(direction: "up" | "down") {
  switch (direction) {
    case "up":
      return 1;
    case "down":
      return -1;
  }
  return error("Should never get here");
}

// 推断的返回类型是 number
function move2(direction: "up" | "down") {
  return direction === "up"
    ? 1
    : direction === "down"
    ? -1
    : error("Should never get here");
}

// 推断的返回类型是 T
function check<T>(x: T | undefined) {
  return x || error("Undefined value");
}

因为 never 可以赋值给任何类型,所以当需要返回更具体类型的回调时,可以使用返回 never 的函数:

ts
function test(cb: () => string) {
  let s = cb();
  return s;
}

test(() => "hello");
test(() => fail());
test(() => {
  throw new Error();
});

只读属性和索引签名

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

只读属性可以有初始化器,并且可以在同一类声明的构造函数中赋值,但除此之外,禁止对只读属性赋值。

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

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

示例
ts
interface Point {
  readonly x: number;
  readonly y: number;
}

var p1: Point = { x: 10, y: 20 };
p1.x = 5; // 错误,p1.x 是只读的

var p2 = { x: 1, y: 1 };
var p3: Point = p2; // 可以,p2 的只读别名
p3.x = 5; // 错误,p3.x 是只读的
p2.x = 5; // 可以,但由于别名也会改变 p3.x
ts
class Foo {
  readonly a = 1;
  readonly b: string;
  constructor() {
    this.b = "hello"; // 构造函数中允许赋值
  }
}
ts
let a: Array<number> = [0, 1, 2, 3, 4];
let b: ReadonlyArray<number> = a;
b[5] = 5; // 错误,元素是只读的
b.push(5); // 错误,没有 push 方法(因为它会改变数组)
b.length = 3; // 错误,length 是只读的
a = b; // 错误,缺少改变数组的方法

为函数指定 this 的类型

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

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

ts
function f(this: void) {
  // 确保在这个独立函数中不能使用 `this`
}

回调中的 this 参数

库还可以使用 this 参数来声明如何调用回调。

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

this: void 表示 addClickListener 期望 onclick 是一个不需要 this 类型的函数。

现在如果你用 this 注释调用代码:

ts
class Handler {
  info: string;
  onClickBad(this: Handler, e: Event) {
    // 哎呀,这里使用了 this。使用此回调将在运行时崩溃
    this.info = e.message;
  }
}
let h = new Handler();
uiElement.addClickListener(h.onClickBad); // 错误!

--noImplicitThis

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

tsconfig.json 中的 Glob 支持

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

两个属性 includeexclude 支持类似 Glob 的文件模式。

示例
json
{
  "compilerOptions": {
    "module": "commonjs",
    "noImplicitAny": true,
    "removeComments": true,
    "preserveConstEnums": true,
    "outFile": "../../built/local/tsc.js",
    "sourceMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "**/*.spec.ts"]
}

支持的 glob 通配符包括:

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

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

如果既没有指定 files 也没有指定 include,编译器默认包含包含目录和子目录中除使用 exclude 属性排除的文件之外的所有 TypeScript(.ts.d.ts.tsx)文件。如果 allowJs 设置为 true,则还会包含 JS 文件(.js.jsx)。

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

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

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

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

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

基本 URL

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

示例
json
{
  "compilerOptions": {
    "baseUrl": "./modules"
  }
}

现在导入 "moduleA" 将在 ./modules/moduleA 中查找。

ts
import A from "moduleA";

路径映射

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

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

示例

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

json
{
  "compilerOptions": {
    "baseUrl": "./node_modules",
    "paths": {
      "jquery": ["jquery/dist/jquery.slim.min"]
    }
}

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

使用 rootDirs 的虚拟目录

使用“rootDirs”,你可以通知编译器构成此“虚拟”目录的; 因此,编译器可以解析这些“虚拟”目录中的相对模块导入,就像它们被合并到一个目录中一样。

示例

给定此项目结构:

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

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

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

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

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

跟踪模块解析

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

shell
tsc --traceResolution

简写环境模块声明

如果你不想在使用新模块之前花时间写出声明,现在可以使用简写声明快速入门。

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

来自简写模块的所有导入都将具有 any 类型。

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

模块名称中的通配符

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

TypeScript 2.0 支持使用通配符(*)声明模块名称的“家族”; 这样,只需要为扩展声明一次,而不是为每个资源。

示例
ts
declare module "*!text" {
  const content: string;
  export default content;
}
// 有些人反过来做。
declare module "json!*" {
  const value: any;
  export default value;
}

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

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

当从无类型代码库迁移时,通配符模块名称甚至更有用。 结合简写环境模块声明,一组模块可以轻松声明为 any

示例
ts
declare module "myLibrary/*";

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

ts
import { readFile } from "myLibrary/fileSystem/readFile`;

readFile(); // readFile 是 'any'

支持 UMD 模块定义

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

例如:

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

然后该库可以作为导入在模块中使用:

ts
import { isPrime } from "math-lib";
isPrime(2);
mathLib.isPrime(2); // 错误:不能在模块内部使用全局定义

它也可以用作全局变量,但只能在脚本中使用。 (脚本是没有导入或导出的文件。)

ts
mathLib.isPrime(2);

可选类属性

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

示例
ts
class Bar {
  a: number;
  b?: number;
  f() {
    return 1;
  }
  g?(): number; // 可选方法的主体可以省略
  h?() {
    return 2;
  }
}

strictNullChecks 模式下编译时,可选属性和方法会自动将 undefined 包含在它们的类型中。因此,上面的 b 属性是 number | undefined 类型,而 g 方法是 (() => number) | undefined 类型。 类型守卫可用于剥离类型中的 undefined 部分:

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
}

私有和受保护构造函数

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

示例
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(); // 错误:'Singleton' 的构造函数是私有的。
let v = Singleton.getInstance();

抽象属性和访问器

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

示例
ts
abstract class Base {
  abstract name: string;
  abstract get value();
  abstract set value(v: number);
}

class Derived extends Base {
  name = "derived";

  value = 1;
}

隐式索引签名

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

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" }); // 可以
httpService("", headers); // 现在可以,以前不行

使用 --lib 包含内置类型声明

获取 ES6/ES2015 内置 API 声明仅限于 target: ES6。 引入 lib;使用 lib,你可以指定要包含在项目中的内置 API 声明组列表。 例如,如果你期望你的运行时支持 MapSetPromise(例如今天的大多数常青浏览器),只需包含 --lib es2015.collection,es2015.promise。 同样,你可以排除你不想在项目中包含的声明,例如,如果你在处理使用 --lib es5,es6 的 node 项目,则可以排除 DOM。

以下是可用 API 组的列表:

  • 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

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

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

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

示例
ts
import B, { readFile } from "./b";
//     ^ 错误:`B` 已声明但从未使用
readFile();

export function write(message: string, args: string[]) {
  //                                 ^^^^  错误:'arg' 已声明但从未使用。
  console.log(message);
}

_ 开头的参数声明不受未使用参数检查的影响。 例如:

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

模块标识符允许使用 .js 扩展名

在 TypeScript 2.0 之前,模块标识符总是被认为是无扩展名的; 例如,给定导入 import d from "./moduleA.js",编译器在 ./moduleA.js.ts./moduleA.js.d.ts 中查找 "moduleA.js" 的定义。 这使得使用期望 URI 作为模块标识符的捆绑/加载工具(如 SystemJS)变得困难。

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

支持 'target : es5' 与 'module: es6'

以前标记为无效标志组合的 target: es5module: es6 现在得到支持。 这应该有助于使用基于 ES2015 的摇树优化工具,如 rollup

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

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

示例
ts
function foo(
  bar: Bar,
  baz: Baz, // 参数列表中的尾随逗号是可以的
) {
  // 实现...
}

foo(
  bar,
  baz, // 参数列表中也允许
);

新的 --skipLibCheck

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

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

允许跨声明的重复标识符

这曾经是重复定义错误的一个常见来源。 多个声明文件在接口上定义相同的成员。

TypeScript 2.0 放宽了这一限制,只要它们具有相同的类型,就允许跨块重复标识符。

在同一块内仍然禁止重复定义。

示例
ts
interface Error {
  stack?: string;
}

interface Error {
  code?: string;
  path?: string;
  stack?: string; // 可以
}

新的 --declarationDir

declarationDir 允许在与 JavaScript 文件不同的位置生成声明文件。