TypeScript 1.8

作为约束的类型参数

在 TypeScript 1.8 中,类型参数约束可以引用同一类型参数列表中的其他类型参数。这在以前是错误的。这种能力通常被称为 F-Bounded 多态 (F-Bounded Polymorphism)

示例
ts
function assign<T extends U, U>(target: T, source: U): T {
for (let id in source) {
target[id] = source[id];
}
return target;
}
let x = { a: 1, b: 2, c: 3, d: 4 };
assign(x, { b: 10, d: 20 });
assign(x, { e: 0 }); // Error

控制流分析错误

TypeScript 1.8 引入了控制流分析,以帮助捕获用户容易遇到的常见错误。请继续阅读以了解更多详情,并查看这些错误的实际表现。

cfa

无法执行的代码

确保在运行时不会执行的语句现在会被正确标记为“无法执行的代码”错误。例如,位于无条件 returnthrowbreakcontinue 语句之后的语句被视为无法执行。使用 allowUnreachableCode 可以禁用对无法执行代码的检测和报告。

示例

这是一个简单的“无法执行代码”错误示例:

ts
function f(x) {
if (x) {
return true;
} else {
return false;
}
x = 0; // Error: Unreachable code detected.
}

此功能捕获的一个更常见的错误是在 return 语句后添加了换行符:

ts
function f() {
return; // Automatic Semicolon Insertion triggered at newline
{
x: "string"; // Error: Unreachable code detected.
}
}

由于 JavaScript 会自动在行尾终止 return 语句,因此该对象字面量变成了一个代码块。

未使用的标签

未使用的标签也会被标记。就像无法执行代码检查一样,这些检查默认是开启的;使用 allowUnusedLabels 可以停止报告这些错误。

示例
ts
loop: while (x > 0) {
// Error: Unused label.
x++;
}

隐式返回

在 JS 中,路径中未返回值的函数会隐式返回 undefined。现在编译器可以将这些情况标记为“隐式返回”。该检查默认是关闭的;使用 noImplicitReturns 来开启它。

示例
ts
function f(x) {
// Error: Not all code paths return a value.
if (x) {
return false;
}
// implicitly returns `undefined`
}

Case 子句穿透 (fall-through)

TypeScript 可以报告 switch 语句中非空 case 子句的穿透错误。此检查默认是关闭的,可以通过 noFallthroughCasesInSwitch 开启。

示例

使用 noFallthroughCasesInSwitch 时,此示例会触发错误:

ts
switch (x % 2) {
case 0: // Error: Fallthrough case in switch.
console.log("even");
case 1:
console.log("odd");
break;
}

然而,在以下示例中,不会报告错误,因为穿透的 case 是空的:

ts
switch (x % 3) {
case 0:
case 1:
console.log("Acceptable");
break;
case 2:
console.log("This is *two much*!");
break;
}

React 中的函数式组件 (Function Components)

TypeScript 现在支持 函数式组件。这些是轻量级组件,可以轻松组合其他组件。

ts
// Use parameter destructuring and defaults for easy definition of 'props' type
const Greeter = ({ name = "world" }) => <div>Hello, {name}!</div>;
// Properties get validated
let example = <Greeter name="TypeScript 1.8" />;

对于此功能和简化的 props,请确保使用 最新版本的 react.d.ts

React 中简化的 props 类型管理

在 TypeScript 1.8 中,配合最新版本的 react.d.ts(见上文),我们还大大简化了 props 类型的声明。

具体来说:

  • 你不再需要显式声明 refkey,也不需要 extend React.Props
  • refkey 属性将以正确的类型出现在所有组件上。
  • 在无状态函数式组件的实例上,ref 属性会被正确禁止。

从模块增强全局/模块作用域

用户现在可以为现有模块声明任何他们想要进行的增强,或者任何其他消费者已经进行的增强。模块增强看起来像普通的旧环境模块声明(即 declare module "foo" { } 语法),并直接嵌套在你自己模块的内部,或者嵌套在另一个顶层环境外部模块中。

此外,TypeScript 还具有 declare global { } 形式的全局增强概念。如果必要,这允许模块增强全局类型,例如 Array

模块增强的名称使用与 importexport 声明中模块标识符相同的规则集来解析。模块增强中的声明与现有声明的合并方式,与它们在同一文件中声明时的合并方式相同。

模块增强和全局增强都不能向顶层作用域添加新项——它们只能“修补”现有声明。

示例

在此,map.ts 可以声明它将在内部修补来自 observable.tsObservable 类型,并向其添加 map 方法。

ts
// observable.ts
export class Observable<T> {
// ...
}
ts
// map.ts
import { Observable } from "./observable";
// Create an augmentation for "./observable"
declare module "./observable" {
// Augment the 'Observable' class definition with interface merging
interface Observable<T> {
map<U>(proj: (el: T) => U): Observable<U>;
}
}
Observable.prototype.map = /*...*/;
ts
// consumer.ts
import { Observable } from "./observable";
import "./map";
let o: Observable<number>;
o.map((x) => x.toFixed());

类似地,全局作用域可以使用 declare global 声明从模块中进行增强。

示例
ts
// Ensure this is treated as a module.
export {};
declare global {
interface Array<T> {
mapToNumbers(): number[];
}
}
Array.prototype.mapToNumbers = function () {
/* ... */
};

字符串字面量类型

API 期望某些特定值的一组字符串并不罕见。例如,考虑一个 UI 库,它可以在屏幕上移动元素,同时控制动画的“缓动”(easing)

ts
declare class UIElement {
animate(options: AnimationOptions): void;
}
interface AnimationOptions {
deltaX: number;
deltaY: number;
easing: string; // Can be "ease-in", "ease-out", "ease-in-out"
}

然而,这很容易出错——没有任何东西能阻止用户意外拼错其中一个有效的缓动值。

ts
// No errors
new UIElement().animate({ deltaX: 100, deltaY: 100, easing: "ease-inout" });

在 TypeScript 1.8 中,我们引入了字符串字面量类型。这些类型的书写方式与字符串字面量相同,但出现在类型位置中。

用户现在可以确保类型系统会捕获此类错误。这是我们使用字符串字面量类型的新 AnimationOptions

ts
interface AnimationOptions {
deltaX: number;
deltaY: number;
easing: "ease-in" | "ease-out" | "ease-in-out";
}
// Error: Type '"ease-inout"' is not assignable to type '"ease-in" | "ease-out" | "ease-in-out"'
new UIElement().animate({ deltaX: 100, deltaY: 100, easing: "ease-inout" });

改进的联合/交叉类型推断

TypeScript 1.8 改进了涉及源端和目标端均为联合或交叉类型的类型推断。例如,当从 string | string[] 推断到 string | T 时,我们将类型简化为 string[]T,从而为 T 推断出 string[]

示例
ts
type Maybe<T> = T | void;
function isDefined<T>(x: Maybe<T>): x is T {
return x !== undefined && x !== null;
}
function isUndefined<T>(x: Maybe<T>): x is void {
return x === undefined || x === null;
}
function getOrElse<T>(x: Maybe<T>, defaultValue: T): T {
return isDefined(x) ? x : defaultValue;
}
function test1(x: Maybe<string>) {
let x1 = getOrElse(x, "Undefined"); // string
let x2 = isDefined(x) ? x : "Undefined"; // string
let x3 = isUndefined(x) ? "Undefined" : x; // string
}
function test2(x: Maybe<number>) {
let x1 = getOrElse(x, -1); // number
let x2 = isDefined(x) ? x : -1; // number
let x3 = isUndefined(x) ? -1 : x; // number
}

使用 --outFile 连接 AMDSystem 模块

在指定 --module amd--module system 的同时指定 outFile,会将编译中的所有模块连接成一个包含多个模块闭包的单一输出文件。

将根据每个模块相对于 rootDir 的位置计算模块名称。

示例
ts
// file src/a.ts
import * as B from "./lib/b";
export function createA() {
return B.createB();
}
ts
// file src/lib/b.ts
export function createB() {
return {};
}

结果为:

js
define("lib/b", ["require", "exports"], function (require, exports) {
"use strict";
function createB() {
return {};
}
exports.createB = createB;
});
define("a", ["require", "exports", "lib/b"], function (require, exports, B) {
"use strict";
function createA() {
return B.createB();
}
exports.createA = createA;
});

支持与 SystemJS 的 default 导入互操作

像 SystemJS 这样的模块加载器会包装 CommonJS 模块并将其作为 ES6 的 default 导入公开。这使得在 SystemJS 和 CommonJS 实现之间共享定义文件变得不可能,因为模块形态在不同的加载器下看起来不同。

设置新的编译器标志 allowSyntheticDefaultImports 表示模块加载器执行某种未在导入的 .ts 或 .d.ts 中指明的合成默认导入成员创建。编译器将推断存在一个具有整个模块形态的 default 导出。

System 模块默认开启此标志。

允许循环中捕获 let/const

之前是错误,现在在 TypeScript 1.8 中得到了支持。循环内且被函数捕获的 let/const 声明现在被编译为正确匹配 let/const 的新鲜度 (freshness) 语义。

示例
ts
let list = [];
for (let i = 0; i < 5; i++) {
list.push(() => i);
}
list.forEach((f) => console.log(f()));

编译为:

js
var list = [];
var _loop_1 = function (i) {
list.push(function () {
return i;
});
};
for (var i = 0; i < 5; i++) {
_loop_1(i);
}
list.forEach(function (f) {
return console.log(f());
});

结果为:

cmd
0
1
2
3
4

改进对 for..in 语句的检查

以前,for..in 变量的类型被推断为 any;这使得编译器忽略了 for..in 主体内的无效使用。

从 TypeScript 1.8 开始:

  • for..in 语句中声明的变量的类型隐式为 string
  • 当一个具有类型 T 的数字索引签名(如数组)的对象被 for..in 变量索引时,且该 for..in 语句的对象拥有数字索引签名而没有字符串索引签名(再次强调如数组),则产生的值的类型为 T
示例
ts
var a: MyObject[];
for (var x in a) {
// Type of x is implicitly string
var obj = a[x]; // Type of obj is MyObject
}

模块现在以 "use strict"; 前导码发出

根据 ES6 标准,模块始终以严格模式解析,但对于非 ES6 目标,生成代码中并未遵循此规则。从 TypeScript 1.8 开始,发出的模块始终处于严格模式。在大多数代码中,这不会产生任何可见的变化,因为 TS 在编译时会将大多数严格模式错误视为错误,但这意味着以前在 TS 代码中静默失败的一些操作(例如分配给 NaN),现在会响亮地失败。您可以参考 MDN 上关于严格模式的文章,以获取严格模式和非严格模式之间差异的详细列表。

使用 --allowJs 包含 .js 文件

项目中通常有并非以 TypeScript 编写的外部源文件。或者,你可能正处于将 JS 代码库转换为 TS 的过程中,但仍希望将所有 JS 代码与新 TS 代码的输出打包到单个文件中。

.js 文件现在被允许作为 tsc 的输入。TypeScript 编译器会检查输入 .js 文件的语法错误,并根据 targetmodule 标志发出有效的输出。输出也可以与其他 .ts 文件组合。Source map 也会像处理 .ts 文件一样为 .js 文件生成。

使用 --reactNamespace 的自定义 JSX 工厂

--jsx react 的同时传递 --reactNamespace <JSX 工厂名称>,允许使用与默认的 React 不同的 JSX 工厂。

新的工厂名称将用于调用 createElement__spread 函数。

示例
ts
import { jsxFactory } from "jsxFactory";
var div = <div>Hello JSX!</div>;

编译时使用:

shell
tsc --jsx react --reactNamespace jsxFactory --m commonJS

结果为:

js
"use strict";
var jsxFactory_1 = require("jsxFactory");
var div = jsxFactory_1.jsxFactory.createElement("div", null, "Hello JSX!");

基于 this 的类型守卫

TypeScript 1.8 将 用户自定义类型守卫函数 扩展到了类和接口的方法。

this is T 现在是类和接口中方法合法的返回类型注解。当在类型收窄位置(例如 if 语句)使用时,调用表达式目标对象的类型将被收窄为 T

示例
ts
class FileSystemObject {
isFile(): this is File {
return this instanceof File;
}
isDirectory(): this is Directory {
return this instanceof Directory;
}
isNetworked(): this is Networked & this {
return this.networked;
}
constructor(public path: string, private networked: boolean) {}
}
class File extends FileSystemObject {
constructor(path: string, public content: string) {
super(path, false);
}
}
class Directory extends FileSystemObject {
children: FileSystemObject[];
}
interface Networked {
host: string;
}
let fso: FileSystemObject = new File("foo/bar.txt", "foo");
if (fso.isFile()) {
fso.content; // fso is File
} else if (fso.isDirectory()) {
fso.children; // fso is Directory
} else if (fso.isNetworked()) {
fso.host; // fso is networked
}

官方 TypeScript NuGet 包

从 TypeScript 1.8 开始,TypeScript 编译器 (tsc.exe) 以及 MSBuild 集成 (Microsoft.TypeScript.targetsMicrosoft.TypeScript.Tasks.dll) 均提供官方 NuGet 包。

稳定版包可在此处获得:

此外,与 每日 npm 包 相匹配的每日 NuGet 包也可在 myget 上获得:

来自 tsc 的更美观错误消息

我们理解大量的单色输出可能会让人视觉疲劳。颜色可以帮助区分消息的开始和结束,当错误输出变得过多时,这些视觉提示非常重要。

只需传递 pretty 命令行选项,TypeScript 就能提供更丰富的彩色输出,并提供有关问题出错位置的上下文。

Showing off pretty error messages in ConEmu

VS 2015 中 JSX 代码的着色

在 TypeScript 1.8 中,JSX 标签现在在 Visual Studio 2015 中进行分类并着色。

jsx

该分类可以通过更改 工具 -> 选项 -> 环境 -> 字体和颜色 页面下的 VB XML 颜色和字体设置来进一步自定义。

--project (-p) 标志现在可以接受任何文件路径

--project 命令行选项最初只能接受包含 tsconfig.json 的文件夹路径。考虑到构建配置的不同场景,允许 --project 指向任何其他兼容的 JSON 文件是有意义的。例如,用户可能希望为 Node 5 使用 CommonJS 模块的 ES2015,但为浏览器使用 AMD 模块的 ES5。通过这项新工作,用户可以轻松仅使用 tsc 管理两个独立的构建目标,而无需执行诸如将 tsconfig.json 文件放置在不同目录之类的笨拙变通方法。

如果给定的是目录,旧行为仍然保持不变——编译器将尝试在该目录中查找名为 tsconfig.json 的文件。

允许在 tsconfig.json 中使用注释

能够记录你的配置总是很好的!tsconfig.json 现在接受单行和多行注释。

{
"": "ES2015", // running on node v5, yaay!
"": true // makes debugging easier
},
/*
* Excluded files
*/
"": ["file.d.ts"]
}

支持输出到 IPC 驱动的文件

TypeScript 1.8 允许用户使用 outFile 参数配合特殊的文件系统实体,如命名管道、设备等。

作为一个例子,在许多类 Unix 系统上,标准输出流可以通过文件 /dev/stdout 访问。

shell
tsc foo.ts --outFile /dev/stdout

这也可用于在命令之间传递输出。

作为一个例子,我们可以将我们发出的 JavaScript 通过管道传递给一个美化打印程序,如 pretty-js

shell
tsc foo.ts --outFile /dev/stdout | pretty-js

Visual Studio 2015 中对 tsconfig.json 的改进支持

TypeScript 1.8 允许在所有项目类型中使用 tsconfig.json 文件。这包括 ASP.NET v4 项目、控制台应用程序带有 TypeScript 的 HTML 应用程序项目类型。此外,你不再局限于单个 tsconfig.json 文件,而是可以添加多个,每个文件都将作为项目的一部分进行构建。这允许你分离应用程序不同部分的配置,而不必使用多个不同的项目。

Showing off tsconfig.json in Visual Studio

当你添加 tsconfig.json 文件时,我们还会禁用项目属性页。这意味着所有配置更改都必须在 tsconfig.json 文件本身中进行。

一些限制

  • 如果你添加了 tsconfig.json 文件,不被视为该上下文一部分的 TypeScript 文件将不会被编译。
  • Apache Cordova 应用仍然存在只能使用单个 tsconfig.json 文件的限制,该文件必须位于根目录或 scripts 文件夹中。
  • 在大多数项目类型中没有 tsconfig.json 的模板。

TypeScript 文档是一个开源项目。请通过发送 Pull Request 来帮助我们改进这些页面

此页面的贡献者
MHMohamed Hegazy (52)
OTOrta Therox (15)
EIEugene Ilyin (1)
NSNick Schonning (1)
JBJack Bates (1)
8+

最后更新:2026 年 3 月 27 日