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 日