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

TypeScript 1.6

JSX 支持

JSX 是一种可嵌入的类似 XML 的语法。 它旨在被转换为有效的 JavaScript,但转换的语义是特定于实现的。 JSX 因 React 库而流行,但此后也有了其他应用。 TypeScript 1.6 支持嵌入、类型检查,并可选地将 JSX 直接编译为 JavaScript。

新的 .tsx 文件扩展名和 as 操作符

TypeScript 1.6 引入了一个新的 .tsx 文件扩展名。 此扩展名有两个作用:它在 TypeScript 文件中启用 JSX,并使新的 as 操作符成为类型断言的默认方式(消除了 JSX 表达式和 TypeScript 前缀类型断言操作符之间的歧义)。 例如:

ts
var x = <any>foo;
// 等价于:
var x = foo as any;

使用 React

要将 JSX 支持与 React 一起使用,你应该使用 React 类型定义。这些类型定义定义了 JSX 命名空间,以便 TypeScript 可以正确检查 React 的 JSX 表达式。例如:

ts
/// <reference path="react.d.ts" />

interface Props {
  name: string;
}

class MyComponent extends React.Component<Props, {}> {
  render() {
    return <span>{this.props.name}</span>;
  }
}

<MyComponent name="bar" />; // 可以
<MyComponent name={0} />; // 错误,`name` 不是数字

使用其他 JSX 框架

JSX 元素名称和属性将根据 JSX 命名空间进行验证。 请参阅 [[JSX]] wiki 页面,了解如何为你的框架定义 JSX 命名空间。

输出生成

TypeScript 附带两种 JSX 模式:preservereact

  • preserve 模式将保留 JSX 表达式作为输出的一部分,供另一个转换步骤进一步使用。此外,输出将具有 .jsx 文件扩展名。
  • react 模式将输出 React.createElement,使用前无需经过 JSX 转换,并且输出将具有 .js 文件扩展名。

有关在 TypeScript 中使用 JSX 的更多信息,请参阅 [[JSX]] wiki 页面。

交叉类型

TypeScript 1.6 引入了交叉类型,它是联合类型的逻辑补充。 联合类型 A | B 表示一个实体要么是类型 A,要么是类型 B,而交叉类型 A & B 表示一个实体既是类型 A 是类型 B

示例
ts
function extend<T, U>(first: T, second: U): T & U {
  let result = <T & U>{};
  for (let id in first) {
    result[id] = first[id];
  }
  for (let id in second) {
    if (!result.hasOwnProperty(id)) {
      result[id] = second[id];
    }
  }
  return result;
}

var x = extend({ a: "hello" }, { b: 42 });
var s = x.a;
var n = x.b;
ts
type LinkedList<T> = T & { next: LinkedList<T> };

interface Person {
  name: string;
}

var people: LinkedList<Person>;
var s = people.name;
var s = people.next.name;
var s = people.next.next.name;
var s = people.next.next.next.name;
ts
interface A {
  a: string;
}
interface B {
  b: string;
}
interface C {
  c: string;
}

var abc: A & B & C;
abc.a = "hello";
abc.b = "hello";
abc.c = "hello";

有关更多信息,请参见 issue #1256

本地类型声明

本地类、接口、枚举和类型别名声明现在可以出现在函数声明内部。本地类型是块级作用域的,类似于用 letconst 声明的变量。例如:

ts
function f() {
  if (true) {
    interface T {
      x: number;
    }
    let v: T;
    v.x = 5;
  } else {
    interface T {
      x: string;
    }
    let v: T;
    v.x = "hello";
  }
}

函数的推断返回类型可能是函数内部本地声明的类型。函数的调用者无法引用这样的本地类型,但当然可以通过结构匹配它。例如:

ts
interface Point {
  x: number;
  y: number;
}

function getPointFactory(x: number, y: number) {
  class P {
    x = x;
    y = y;
  }
  return P;
}

var PointZero = getPointFactory(0, 0);
var PointOne = getPointFactory(1, 1);
var p1 = new PointZero();
var p2 = new PointZero();
var p3 = new PointOne();

本地类型可以引用外部的类型参数,并且本地类和接口本身也可以是泛型的。例如:

ts
function f3() {
  function f<X, Y>(x: X, y: Y) {
    class C {
      public x = x;
      public y = y;
    }
    return C;
  }
  let C = f(10, "hello");
  let v = new C();
  let x = v.x; // number
  let y = v.y; // string
}

类表达式

TypeScript 1.6 增加了对 ES6 类表达式的支持。在类表达式中,类名是可选的,并且如果指定了,则仅在类表达式本身的作用域内有效。这与函数表达式的可选名称类似。在类表达式外部无法引用类表达式的类实例类型,但该类型当然可以通过结构匹配。例如:

ts
let Point = class {
  constructor(public x: number, public y: number) {}
  public length() {
    return Math.sqrt(this.x * this.x + this.y * this.y);
  }
};
var p = new Point(3, 4); // p 具有匿名类类型
console.log(p.length());

扩展表达式

TypeScript 1.6 增加了对类扩展任意表达式的支持,该表达式计算出一个构造函数。这意味着内置类型现在可以在类声明中被扩展。

以前,类的 extends 子句需要指定一个类型引用。现在它接受一个可选的表达式,后面可跟类型参数列表。表达式的类型必须是一个构造函数类型,至少有一个构造签名,该构造签名的类型参数数量与 extends 子句中指定的类型参数数量相同。匹配的构造签名的返回类型是类实例类型继承的基类型。实际上,这允许在 extends 子句中指定真正的类和“类”表达式。

一些示例:

ts
// 扩展内置类型

class MyArray extends Array<number> {}
class MyError extends Error {}

// 扩展计算出的基类

class ThingA {
  getGreeting() {
    return "Hello from A";
  }
}

class ThingB {
  getGreeting() {
    return "Hello from B";
  }
}

interface Greeter {
  getGreeting(): string;
}

interface GreeterConstructor {
  new (): Greeter;
}

function getGreeterBase(): GreeterConstructor {
  return Math.random() >= 0.5 ? ThingA : ThingB;
}

class Test extends getGreeterBase() {
  sayHello() {
    console.log(this.getGreeting());
  }
}

abstract 类和方法

TypeScript 1.6 增加了对 abstract 关键字用于类及其方法的支持。抽象类允许包含没有实现的方法,并且不能被实例化。

示例
ts
abstract class Base {
  abstract getThing(): string;
  getOtherThing() {
    return "hello";
  }
}

let x = new Base(); // 错误,'Base' 是抽象的

// 错误,必须要么是 'abstract' 要么实现具体的 'getThing'
class Derived1 extends Base {}

class Derived2 extends Base {
  getThing() {
    return "hello";
  }
  foo() {
    super.getThing(); // 错误:不能通过 'super' 调用抽象成员
  }
}

var x = new Derived2(); // 可以
var y: Base = new Derived2(); // 也可以
y.getThing(); // 可以
y.getOtherThing(); // 可以

泛型类型别名

使用 TypeScript 1.6,类型别名可以是泛型的。例如:

ts
type Lazy<T> = T | (() => T);

var s: Lazy<string>;
s = "eager";
s = () => "lazy";

interface Tuple<A, B> {
  a: A;
  b: B;
}

type Pair<T> = Tuple<T, T>;

更严格的对象字面量赋值检查

TypeScript 1.6 为了捕获多余或拼写错误的属性,对对象字面量赋值实施了更严格的检查。具体来说,当一个全新的对象字面量被赋值给一个变量或作为非空目标类型的参数传递时,如果对象字面量指定了目标类型中不存在的属性,则为错误。

示例
ts
var x: { foo: number };
x = { foo: 1, baz: 2 }; // 错误,多余的属性 `baz`

var y: { foo: number; bar?: number };
y = { foo: 1, baz: 2 }; // 错误,多余或拼写错误的属性 `baz`

类型可以包含索引签名以显式允许多余属性:

ts
var x: { foo: number; [x: string]: any };
x = { foo: 1, baz: 2 }; // 可以,`baz` 与索引签名匹配

ES6 生成器

TypeScript 1.6 增加了在以 ES6 为目标时对生成器的支持。

生成器函数可以有返回类型注解,就像普通函数一样。该注解表示函数返回的生成器的类型。这里是一个示例:

ts
function* g(): Iterable<string> {
  for (var i = 0; i < 100; i++) {
    yield ""; // string 可赋值给 string
  }
  yield* otherStringGenerator(); // otherStringGenerator 必须是可迭代的,且元素类型可赋值给 string
}

没有类型注解的生成器函数可以推断出类型注解。 因此在以下情况下,类型将从 yield 语句推断:

ts
function* g() {
  for (var i = 0; i < 100; i++) {
    yield ""; // 推断为 string
  }
  yield* otherStringGenerator(); // 推断 otherStringGenerator 的元素类型
}

async 函数的实验性支持

TypeScript 1.6 在目标为 ES6 时引入了对 async 函数的实验性支持。 异步函数预期调用一个异步操作并等待其结果,而不会阻塞程序的正常执行。 这是通过使用一个兼容 ES6 的 Promise 实现,并将函数体转换为一种兼容形式来实现的,以便在等待的异步操作完成时恢复执行。

异步函数是一个以 async 修饰符为前缀的函数或方法。该修饰符告知编译器需要进行函数体转换,并且关键字 await 应被视为一元表达式而不是标识符。 异步函数必须提供一个指向兼容 Promise 类型的返回类型注解。只有在全局定义了兼容的 Promise 类型时,才能使用返回类型推断。

示例
ts
var p: Promise<number> = /* ... */;
async function fn(): Promise<number> {
  var i = await p; // 暂停执行直到 'p' 被解决。'i' 的类型为 "number"
  return 1 + i;
}

var a = async (): Promise<number> => 1 + await p; // 暂停执行。
var a = async () => 1 + await p; // 暂停执行。当使用 --target ES6 编译时,返回类型推断为 "Promise<number>"
var fe = async function(): Promise<number> {
  var i = await p; // 暂停执行直到 'p' 被解决。'i' 的类型为 "number"
  return 1 + i;
}

class C {
  async m(): Promise<number> {
    var i = await p; // 暂停执行直到 'p' 被解决。'i' 的类型为 "number"
    return 1 + i;
  }

  async get p(): Promise<number> {
    var i = await p; // 暂停执行直到 'p' 被解决。'i' 的类型为 "number"
    return 1 + i;
  }
}

夜间构建

虽然不是严格意义上的语言更改,但现在已经可以通过以下命令安装夜间构建:

Shell
npm install -g typescript@next

模块解析逻辑的调整

从 1.6 版本开始,当目标为 'commonjs' 时,TypeScript 编译器将使用不同的规则集来解析模块名称。 这些规则试图模拟 Node 使用的模块查找过程。 这实际上意味着 node 模块可以包含其类型信息,并且 TypeScript 编译器将能够找到它。 用户仍然可以通过使用 moduleResolution 命令行选项来覆盖编译器选择的模块解析规则。可能的值有:

  • 'classic' - 1.6 之前的 TypeScript 编译器使用的模块解析规则
  • 'node' - 类似 Node 的模块解析

环境类与接口声明合并

环境类声明的实例侧可以使用接口声明进行扩展。类构造函数对象保持不变。 例如:

ts
declare class Foo {
  public x: number;
}

interface Foo {
  y: string;
}

function bar(foo: Foo) {
  foo.x = 1; // 可以,在类 Foo 中声明
  foo.y = "1"; // 可以,在接口 Foo 中声明
}

用户定义的类型守卫函数

TypeScript 1.6 在 typeofinstanceof 之外,增加了一种在 if 块内部收窄变量类型的新方法。 用户定义的类型守卫函数是指其返回类型注解形式为 x is T 的函数,其中 x 是签名中的一个声明参数,T 是任何类型。 当在 if 块中对一个变量调用用户定义的类型守卫函数时,该变量的类型将被收窄为 T

示例
ts
function isCat(a: any): a is Cat {
  return a.name === "kitty";
}

var x: Cat | Dog;
if (isCat(x)) {
  x.meow(); // 可以,在此块中 x 是 Cat
}

tsconfig.json 中的 exclude 属性支持

一个没有指定 files 属性(因此隐式引用所有子目录中的所有 *.ts 文件)的 tsconfig.json 文件现在可以包含一个 exclude 属性,该属性指定要从编译中排除的文件和/或目录列表。 exclude 属性必须是一个字符串数组,每个字符串指定相对于 tsconfig.json 文件位置的文件或文件夹名称。 例如:

json
{
  "compilerOptions": {
    "out": "test.js"
  },
  "exclude": ["node_modules", "test.ts", "utils/t2.ts"]
}

exclude 列表不支持通配符。它必须只是文件和/或目录的列表。

--init 命令行选项

在目录中运行 tsc --init 将在该目录中创建一个带有预设默认值的初始 tsconfig.json。 可选地,可以传递命令行参数与 --init 一起,这些参数将在创建时存储在你的初始 tsconfig.json 中。