JSX 支持
JSX 是一种可嵌入的类 XML 语法。它旨在被转换为有效的 JavaScript,但转换的具体语义取决于实现。JSX 随着 React 库的普及而流行,此后也看到了其他应用。TypeScript 1.6 支持嵌入、类型检查以及可选地直接将 JSX 编译为 JavaScript。
新的 .tsx 文件扩展名和 as 运算符
TypeScript 1.6 引入了新的 .tsx 文件扩展名。此扩展名有两层含义:它允许在 TypeScript 文件中使用 JSX,并使新的 as 运算符成为类型转换的默认方式(消除了 JSX 表达式与 TypeScript 前缀类型转换运算符之间的任何歧义)。例如
tsvar x = <any>foo;// is equivalent to:var x = foo as any;
使用 React
若要在 React 中使用 JSX 支持,您应该使用 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" />; // OK<MyComponent name={0} />; // error, `name` is not a number
使用其他 JSX 框架
JSX 元素名称和属性会根据 JSX 命名空间进行验证。请参阅 [[JSX]] Wiki 页面,了解如何为您的框架定义 JSX 命名空间。
输出生成
TypeScript 附带两种 JSX 模式:preserve 和 react。
preserve模式会将 JSX 表达式保留在输出中,供后续的转换步骤处理。此外,输出将具有.jsx文件扩展名。react模式将发出React.createElement调用,使用前无需经过 JSX 转换,且输出将具有.js文件扩展名。
有关在 TypeScript 中使用 JSX 的更多信息,请参阅 [[JSX]] Wiki 页面。
交集类型 (Intersection types)
TypeScript 1.6 引入了交集类型,它是联合类型的逻辑补集。联合类型 A | B 表示属于 A 或 B 类型中的一种,而交集类型 A & B 表示同时属于 A 和 B 类型的实体。
示例
tsfunction 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;
tstype 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;
tsinterface 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。
局部类型声明
现在的函数声明内部可以出现局部类、接口、枚举和类型别名声明。局部类型是块级作用域的,类似于用 let 和 const 声明的变量。例如
tsfunction f() {if (true) {interface T {x: number;}let v: T;v.x = 5;} else {interface T {x: string;}let v: T;v.x = "hello";}}
函数的推断返回类型可能是函数内部声明的局部类型。函数的调用者无法引用此类局部类型,但当然可以通过结构匹配来兼容。例如
tsinterface 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();
局部类型可以引用封闭的类型参数,局部类和接口本身也可以是泛型的。例如
tsfunction 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; // numberlet y = v.y; // string}
类表达式
TypeScript 1.6 增加了对 ES6 类表达式的支持。在类表达式中,类名是可选的;如果指定了类名,它仅在类表达式自身的作用域内有效。这类似于函数表达式的可选名称。在类表达式之外无法引用类表达式的类实例类型,但当然可以通过结构匹配来兼容。例如
tslet 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 has anonymous class typeconsole.log(p.length());
扩展表达式
TypeScript 1.6 增加了对类扩展任意表达式的支持,该表达式会计算出一个构造函数。这意味着内置类型现在可以在类声明中被扩展。
类之前的 extends 子句需要指定一个类型引用。现在它接受一个表达式,后面可选跟类型参数列表。该表达式的类型必须是一个构造函数类型,并且至少有一个构造签名,其类型参数数量与 extends 子句中指定的类型参数数量相同。匹配的构造签名的返回类型是类实例类型继承自的基类型。实际上,这允许在 extends 子句中指定真实的类和“类表达式”。
一些示例
ts// Extend built-in typesclass MyArray extends Array<number> {}class MyError extends Error {}// Extend computed base classclass 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 关键字支持。抽象类允许拥有未实现的方法,且无法被实例化。
示例
tsabstract class Base {abstract getThing(): string;getOtherThing() {return "hello";}}let x = new Base(); // Error, 'Base' is abstract// Error, must either be 'abstract' or implement concrete 'getThing'class Derived1 extends Base {}class Derived2 extends Base {getThing() {return "hello";}foo() {super.getThing(); // Error: cannot invoke abstract members through 'super'}}var x = new Derived2(); // OKvar y: Base = new Derived2(); // Also OKy.getThing(); // OKy.getOtherThing(); // OK
泛型类型别名
在 TypeScript 1.6 中,类型别名可以是泛型的。例如
tstype 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 强制执行更严格的对象字面量赋值检查,旨在捕获多余或拼写错误的属性。具体而言,当一个新的对象字面量被赋值给变量或作为非空目标类型的参数传递时,如果对象字面量指定了目标类型中不存在的属性,则会报错。
示例
tsvar x: { foo: number };x = { foo: 1, baz: 2 }; // Error, excess property `baz`var y: { foo: number; bar?: number };y = { foo: 1, baz: 2 }; // Error, excess or misspelled property `baz`
类型可以包含索引签名,以明确指示允许存在额外的属性
tsvar x: { foo: number; [x: string]: any };x = { foo: 1, baz: 2 }; // Ok, `baz` matched by index signature
ES6 生成器 (Generators)
TypeScript 1.6 增加了在面向 ES6 时对生成器的支持。
生成器函数可以像普通函数一样带有返回类型注解。该注解表示函数返回的生成器的类型。这是一个示例
tsfunction* g(): Iterable<string> {for (var i = 0; i < 100; i++) {yield ""; // string is assignable to string}yield* otherStringGenerator(); // otherStringGenerator must be iterable and element type assignable to string}
没有类型注解的生成器函数可以进行类型推断。因此在下例中,类型将根据 yield 语句进行推断
tsfunction* g() {for (var i = 0; i < 100; i++) {yield ""; // infer string}yield* otherStringGenerator(); // infer element type of otherStringGenerator}
对 async 函数的实验性支持
TypeScript 1.6 引入了在面向 ES6 时对 async 函数的实验性支持。异步函数旨在调用异步操作并等待其结果,而不阻塞程序的正常执行。这是通过使用 ES6 兼容的 Promise 实现,并将函数体转换为兼容形式,以便在异步操作完成后恢复执行来实现的。
异步函数是指带有 async 修饰符的函数或方法。此修饰符通知编译器需要进行函数体转换,并将关键字 await 视为一元表达式而不是标识符。异步函数必须提供一个指向兼容 Promise 类型的返回类型注解。只有在全局定义了兼容的 Promise 类型时,才能使用返回类型推断。
示例
tsvar p: Promise<number> = /* ... */;async function fn(): Promise<number> {var i = await p; // suspend execution until 'p' is settled. 'i' has type "number"return 1 + i;}var a = async (): Promise<number> => 1 + await p; // suspends execution.var a = async () => 1 + await p; // suspends execution. return type is inferred as "Promise<number>" when compiling with --target ES6var fe = async function(): Promise<number> {var i = await p; // suspend execution until 'p' is settled. 'i' has type "number"return 1 + i;}class C {async m(): Promise<number> {var i = await p; // suspend execution until 'p' is settled. 'i' has type "number"return 1 + i;}async get p(): Promise<number> {var i = await p; // suspend execution until 'p' is settled. 'i' has type "number"return 1 + i;}}
夜间构建版本 (Nightly builds)
虽然不是严格意义上的语言变更,但现在可以通过以下命令安装夜间构建版本
npm install -g typescript@next
模块解析逻辑的调整
从 1.6 版本开始,当面向 'commonjs' 时,TypeScript 编译器将使用一套不同的规则来解析模块名称。这些 规则 旨在模拟 Node 使用的模块查找过程。这实际上意味着 node 模块可以包含其类型定义信息,而 TypeScript 编译器将能够找到它。不过,用户可以通过使用 moduleResolution 命令行选项来覆盖编译器选择的模块解析规则。可选值为
- 'classic' - 1.6 版本之前的 TypeScript 编译器使用的模块解析规则
- 'node' - 类 Node 的模块解析
合并环境类与接口声明
环境类声明的实例侧可以使用接口声明进行扩展。类构造函数对象保持不变。例如
tsdeclare class Foo {public x: number;}interface Foo {y: string;}function bar(foo: Foo) {foo.x = 1; // OK, declared in the class Foofoo.y = "1"; // OK, declared in the interface Foo}
用户自定义类型保护函数
TypeScript 1.6 增加了一种在 if 块内收窄变量类型的新方法,作为 typeof 和 instanceof 的补充。用户自定义类型保护函数是返回类型注解形式为 x is T 的函数,其中 x 是签名中的已声明参数,T 是任意类型。当在 if 块中对变量调用用户自定义类型保护函数时,该变量的类型将被收窄为 T。
示例
tsfunction isCat(a: any): a is Cat {return a.name === "kitty";}var x: Cat | Dog;if (isCat(x)) {x.meow(); // OK, x is Cat in this block}
tsconfig.json 中的 exclude 属性支持
未指定 files 属性的 tsconfig.json 文件(因此隐式引用所有子目录中的所有 *.ts 文件)现在可以包含一个 exclude 属性,用于指定要从编译中排除的文件和/或目录列表。exclude 属性必须是一个字符串数组,每个字符串指定一个相对于 tsconfig.json 文件位置的文件或文件夹名称。例如
{"": {"": "test.js"},"": ["node_modules", "test.ts", "utils/t2.ts"]}
exclude 列表不支持通配符。它必须只是一个文件和/或目录的列表。
--init 命令行选项
在目录中运行 tsc --init 可在该目录中创建一个包含预设默认值的初始 tsconfig.json。您可以选择在 --init 后面传递命令行参数,以便在创建时将其存入初始的 tsconfig.json 中。