在 TypeScript 1.8 中, 類型參數(shù)的限制可以引用自同一個(gè)類型參數(shù)列表中的類型參數(shù). 在此之前這種做法會(huì)報(bào)錯(cuò). 這種特性通常被叫做 F-Bounded Polymorphism.
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 }); // 錯(cuò)誤
TypeScript 1.8 中引入了控制流分析來捕獲開發(fā)者通常會(huì)遇到的一些錯(cuò)誤.
詳情見接下來的內(nèi)容, 可以上手嘗試:

一定無法在運(yùn)行時(shí)被執(zhí)行的語句現(xiàn)在會(huì)被標(biāo)記上代碼不可及錯(cuò)誤. 舉個(gè)例子, 在無條件限制的 return, throw, break 或者 continue 后的語句被認(rèn)為是不可及的. 使用 --allowUnreachableCode 來禁用不可及代碼的檢測和報(bào)錯(cuò).
這里是一個(gè)簡單的不可及錯(cuò)誤的例子:
function f(x) {
if (x) {
return true;
}
else {
return false;
}
x = 0; // 錯(cuò)誤: 檢測到不可及的代碼.
}
這個(gè)特性能捕獲的一個(gè)更常見的錯(cuò)誤是在 return 語句后添加換行:
function f() {
return // 換行導(dǎo)致自動(dòng)插入的分號(hào)
{
x: "string" // 錯(cuò)誤: 檢測到不可及的代碼.
}
}
因?yàn)?JavaScript 會(huì)自動(dòng)在行末結(jié)束 return 語句, 下面的對(duì)象字面量變成了一個(gè)代碼塊.
未使用的標(biāo)簽也會(huì)被標(biāo)記. 和不可及代碼檢查一樣, 被使用的標(biāo)簽檢查也是默認(rèn)開啟的. 使用 --allowUnusedLabels 來禁用未使用標(biāo)簽的報(bào)錯(cuò).
loop: while (x > 0) { // 錯(cuò)誤: 未使用的標(biāo)簽.
x++;
}
JS 中沒有返回值的代碼分支會(huì)隱式地返回 undefined. 現(xiàn)在編譯器可以將這種方式標(biāo)記為隱式返回. 對(duì)于隱式返回的檢查默認(rèn)是被禁用的, 可以使用 --noImplicitReturns 來啟用.
function f(x) { // 錯(cuò)誤: 不是所有分支都返回了值.
if (x) {
return false;
}
// 隱式返回了 `undefined`
}
TypeScript 現(xiàn)在可以在 switch 語句中出現(xiàn)貫穿的幾個(gè)非空 case 時(shí)報(bào)錯(cuò).
這個(gè)檢測默認(rèn)是關(guān)閉的, 可以使用 --noFallthroughCasesInSwitch 啟用.
switch (x % 2) {
case 0: // 錯(cuò)誤: switch 中出現(xiàn)了貫穿的 case.
console.log("even");
case 1:
console.log("odd");
break;
}
然而, 在下面的例子中, 由于貫穿的 case 是空的, 并不會(huì)報(bào)錯(cuò):
switch (x % 3) {
case 0:
case 1:
console.log("Acceptable");
break;
case 2:
console.log("This is *two much*!");
break;
}
TypeScript 現(xiàn)在支持無狀態(tài)的函數(shù)組件. 它是可以組合其他組件的輕量級(jí)組件.
// 使用參數(shù)解構(gòu)和默認(rèn)值輕松地定義 'props' 的類型
const Greeter = ({name = 'world'}) => <div>Hello, {name}!</div>;
// 參數(shù)可以被檢驗(yàn)
let example = <Greeter name='TypeScript 1.8' />;
如果需要使用這一特性及簡化的 props, 請確認(rèn)使用的是最新的 react.d.ts.
props 類型管理在 TypeScript 1.8 配合最新的 react.d.ts (見上方) 大幅簡化了 props 的類型聲明.
具體的:
ref 和 key 或者 extend React.Propsref 和 key 屬性會(huì)在所有組件上擁有正確的類型.ref 屬性在無狀態(tài)函數(shù)組件上會(huì)被正確地禁用.用戶現(xiàn)在可以為任何模塊進(jìn)行他們想要, 或者其他人已經(jīng)對(duì)其作出的擴(kuò)充.
模塊擴(kuò)充的形式和過去的包模塊一致 (例如 declare module "foo" { } 這樣的語法), 并且可以直接嵌在你自己的模塊內(nèi), 或者在另外的頂級(jí)外部包模塊中.
除此之外, TypeScript 還以 declare global { } 的形式提供了對(duì)于_全局_聲明的擴(kuò)充.
這能使模塊對(duì)像 Array 這樣的全局類型在必要的時(shí)候進(jìn)行擴(kuò)充.
模塊擴(kuò)充的名稱解析規(guī)則與 import 和 export 聲明中的一致.
擴(kuò)充的模塊聲明合并方式與在同一個(gè)文件中聲明是相同的.
不論是模塊擴(kuò)充還是全局聲明擴(kuò)充都不能向頂級(jí)作用域添加新的項(xiàng)目 - 它們只能為已經(jīng)存在的聲明添加 "補(bǔ)丁".
這里的 map.ts 可以聲明它會(huì)在內(nèi)部修改在 observable.ts 中聲明的 Observable 類型, 添加 map 方法.
// observable.ts
export class Observable<T> {
// ...
}
// map.ts
import { Observable } from "./observable";
// 擴(kuò)充 "./observable"
declare module "./observable" {
// 使用接口合并擴(kuò)充 'Observable' 類的定義
interface Observable<T> {
map<U>(proj: (el: T) => U): Observable<U>;
}
}
Observable.prototype.map = /*...*/;
// consumer.ts
import { Observable } from "./observable";
import "./map";
let o: Observable<number>;
o.map(x => x.toFixed());
相似的, 在模塊中全局作用域可以使用 declare global 聲明被增強(qiáng):
// 確保當(dāng)前文件被當(dāng)做一個(gè)模塊.
export {};
declare global {
interface Array<T> {
mapToNumbers(): number[];
}
}
Array.prototype.mapToNumbers = function () { /* ... */ }
接受一個(gè)特定字符串集合作為某個(gè)值的 API 并不少見. 舉例來說, 考慮一個(gè)可以通過控制動(dòng)畫的漸變讓元素在屏幕中滑動(dòng)的 UI 庫:
declare class UIElement {
animate(options: AnimationOptions): void;
}
interface AnimationOptions {
deltaX: number;
deltaY: number;
easing: string; // 可以是 "ease-in", "ease-out", "ease-in-out"
}
然而, 這容易產(chǎn)生錯(cuò)誤 - 當(dāng)用戶錯(cuò)誤不小心錯(cuò)誤拼寫了一個(gè)合法的值時(shí), 并沒有任何提示:
// 沒有報(bào)錯(cuò)
new UIElement().animate({ deltaX: 100, deltaY: 100, easing: "ease-inout" });
在 TypeScript 1.8 中, 我們新增了字符串字面量類型. 這些類型和字符串字面量的寫法一致, 只是寫在類型的位置.
用戶現(xiàn)在可以確保類型系統(tǒng)會(huì)捕獲這樣的錯(cuò)誤.
這里是我們使用了字符串字面量類型的新的 AnimationOptions:
interface AnimationOptions {
deltaX: number;
deltaY: number;
easing: "ease-in" | "ease-out" | "ease-in-out";
}
// 錯(cuò)誤: 類型 '"ease-inout"' 不能復(fù)制給類型 '"ease-in" | "ease-out" | "ease-in-out"'
new UIElement().animate({ deltaX: 100, deltaY: 100, easing: "ease-inout" });
TypeScript 1.8 優(yōu)化了源類型和目標(biāo)類型都是聯(lián)合或者交叉類型的情況下的類型推導(dǎo).
舉例來說, 當(dāng)從 string | string[] 推導(dǎo)到 string | T 時(shí), 我們將類型拆解為 string[] 和 T, 這樣就可以將 string[] 推導(dǎo)為 T.
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 合并 AMD 和 System 模塊在使用 --module amd 或者 --module system 的同時(shí)制定 --outFile 將會(huì)把所有參與編譯的模塊合并為單個(gè)包括了多個(gè)模塊閉包的輸出文件.
每一個(gè)模塊都會(huì)根據(jù)其相對(duì)于 rootDir 的位置被計(jì)算出自己的模塊名稱.
// 文件 src/a.ts
import * as B from "./lib/b";
export function createA() {
return B.createB();
}
// 文件 src/lib/b.ts
export function createB() {
return { };
}
結(jié)果為:
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;
});
default 導(dǎo)入像 SystemJS 這樣的模塊加載器將 CommonJS 模塊做了包裝并暴露為 default ES6 導(dǎo)入項(xiàng). 這使得在 SystemJS 和 CommonJS 的實(shí)現(xiàn)由于不同加載器不同的模塊導(dǎo)出方式不能共享定義.
設(shè)置新的編譯選項(xiàng) --allowSyntheticDefaultImports 指明模塊加載器會(huì)進(jìn)行導(dǎo)入的 .ts 或 .d.ts 中未指定的某種類型的默認(rèn)導(dǎo)入項(xiàng)構(gòu)建. 編譯器會(huì)由此推斷存在一個(gè) default 導(dǎo)出項(xiàng)和整個(gè)模塊自己一致.
此選項(xiàng)在 System 模塊默認(rèn)開啟.
let/const之前這樣會(huì)報(bào)錯(cuò), 現(xiàn)在由 TypeScript 1.8 支持.
循環(huán)中被函數(shù)引用的 let/const 聲明現(xiàn)在會(huì)被輸出為與 let/const 更新語義相符的代碼.
let list = [];
for (let i = 0; i < 5; i++) {
list.push(() => i);
}
list.forEach(f => console.log(f()));
被編譯為:
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()); });
然后結(jié)果是:
0
1
2
3
4
for..in 語句檢查過去 for..in 變量的類型被推斷為 any, 這使得編譯器忽略了 for..in 語句內(nèi)的一些不合法的使用.
從 TypeScript 1.8 開始:
for..in 語句中的變量隱含類型為 string.T (比如一個(gè)數(shù)組) 的對(duì)象被一個(gè) for..in 索引有數(shù)字索引簽名并且沒有字符串索引簽名 (比如還是數(shù)組) 的對(duì)象的變量索引, 產(chǎn)生的值的類型為 T.var a: MyObject[];
for (var x in a) { // x 的隱含類型為 string
var obj = a[x]; // obj 的類型為 MyObject
}
"use strict;"對(duì)于 ES6 來說模塊始終以嚴(yán)格模式被解析, 但這一點(diǎn)過去對(duì)于非 ES6 目標(biāo)在生成的代碼中并沒有遵循. 從 TypeScript 1.8 開始, 輸出的模塊總會(huì)為嚴(yán)格模式. 由于多數(shù)嚴(yán)格模式下的錯(cuò)誤也是 TS 編譯時(shí)的錯(cuò)誤, 多數(shù)代碼并不會(huì)有可見的改動(dòng), 但是這也意味著有一些東西可能在運(yùn)行時(shí)沒有征兆地失敗, 比如賦值給 NaN 現(xiàn)在會(huì)有運(yùn)行時(shí)錯(cuò)誤. 你可以參考這篇 MDN 上的文章 查看詳細(xì)的嚴(yán)格模式與非嚴(yán)格模式的區(qū)別列表.
--allowJs 加入 .js 文件經(jīng)常在項(xiàng)目中會(huì)有外部的非 TypeScript 編寫的源文件. 一種方式是將 JS 代碼轉(zhuǎn)換為 TS 代碼, 但這時(shí)又希望將所有 JS 代碼和新的 TS 代碼的輸出一起打包為一個(gè)文件.
.js 文件現(xiàn)在允許作為 tsc 的輸入文件. TypeScript 編譯器會(huì)檢查 .js 輸入文件的語法錯(cuò)誤, 并根據(jù) --target 和 --module 選項(xiàng)輸出對(duì)應(yīng)的代碼.
輸出也會(huì)和其他 .ts 文件一起. .js 文件的 source maps 也會(huì)像 .ts 文件一樣被生成.
--reactNamespace 自定義 JSX 工廠在使用 --jsx react 的同時(shí)使用 --reactNamespace <JSX 工廠名稱> 可以允許使用一個(gè)不同的 JSX 工廠代替默認(rèn)的 React.
新的工廠名稱會(huì)被用來調(diào)用 createElement 和 __spread 方法.
import {jsxFactory} from "jsxFactory";
var div = <div>Hello JSX!</div>
編譯參數(shù):
tsc --jsx react --reactNamespace jsxFactory --m commonJS
結(jié)果:
"use strict";
var jsxFactory_1 = require("jsxFactory");
var div = jsxFactory_1.jsxFactory.createElement("div", null, "Hello JSX!");
this 的類型收窄TypeScript 1.8 為類和接口方法擴(kuò)展了用戶定義的類型收窄函數(shù).
this is T 現(xiàn)在是類或接口方法的合法的返回值類型標(biāo)注.
當(dāng)在類型收窄的位置使用時(shí) (比如 if 語句), 函數(shù)調(diào)用表達(dá)式的目標(biāo)對(duì)象的類型會(huì)被收窄為 T.
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 是 File
}
else if (fso.isDirectory()) {
fso.children; // fso 是 Directory
}
else if (fso.isNetworked()) {
fso.host; // fso 是 networked
}
從 TypeScript 1.8 開始, 將為 TypeScript 編譯器 (tsc.exe) 和 MSBuild 整合 (Microsoft.TypeScript.targets 和 Microsoft.TypeScript.Tasks.dll) 提供官方的 NuGet 包.
穩(wěn)定版本可以在這里下載:
與此同時(shí), 和每日 npm 包對(duì)應(yīng)的每日 NuGet 包可以在 https://myget.org 下載:
tsc 錯(cuò)誤信息更美觀我們理解大量單色的輸出并不直觀. 顏色可以幫助識(shí)別信息的始末, 這些視覺上的線索在處理復(fù)雜的錯(cuò)誤信息時(shí)非常重要.
通過傳遞 --pretty 命令行選項(xiàng), TypeScript 會(huì)給出更豐富的輸出, 包含錯(cuò)誤發(fā)生的上下文.

在 TypeScript 1.8 中, JSX 標(biāo)簽現(xiàn)在可以在 Visual Studio 2015 中被分別和高亮.

通過 工具->選項(xiàng)->環(huán)境->字體與顏色 頁面在 VB XML 顏色和字體設(shè)置中還可以進(jìn)一步改變字體和顏色來自定義.
--project (-p) 選項(xiàng)現(xiàn)在接受任意文件路徑--project 命令行選項(xiàng)過去只接受包含了 tsconfig.json 文件的文件夾.
考慮到不同的構(gòu)建場景, 應(yīng)該允許 --project 指向任何兼容的 JSON 文件.
比如說, 一個(gè)用戶可能會(huì)希望為 Node 5 編譯 CommonJS 的 ES 2015, 為瀏覽器編譯 AMD 的 ES5.
現(xiàn)在少了這項(xiàng)限制, 用戶可以更容易地直接使用 tsc 管理不同的構(gòu)建目標(biāo), 無需再通過一些奇怪的方式, 比如將多個(gè) tsconfig.json 文件放在不同的目錄中.
如果參數(shù)是一個(gè)路徑, 行為保持不變 - 編譯器會(huì)嘗試在該目錄下尋找名為 tsconfig.json 的文件.
為配置添加文檔是很棒的! tsconfig.json 現(xiàn)在支持單行和多行注釋.
{
"compilerOptions": {
"target": "ES2015", // 跑在 node v5 上, 呀!
"sourceMap": true // 讓調(diào)試輕松一些
},
/*
* 排除的文件
*/
"exclude": [
"file.d.ts"
]
}
TypeScript 1.8 允許用戶將 --outFile 參數(shù)和一些特殊的文件系統(tǒng)對(duì)象一起使用, 比如命名的管道 (pipe), 設(shè)備 (devices) 等.
舉個(gè)例子, 在很多與 Unix 相似的系統(tǒng)上, 標(biāo)準(zhǔn)輸出流可以通過文件 /dev/stdout 訪問.
tsc foo.ts --outFile /dev/stdout
這一特性也允許輸出給其他命令.
比如說, 我們可以輸出生成的 JavaScript 給一個(gè)像 pretty-js 這樣的格式美化工具:
tsc foo.ts --outFile /dev/stdout | pretty-js
tsconfig.json 的支持TypeScript 1.8 允許在任何種類的項(xiàng)目中使用 tsconfig.json 文件.
包括 ASP.NET v4 項(xiàng)目, 控制臺(tái)應(yīng)用, 以及 用 TypeScript 開發(fā)的 HTML 應(yīng)用.
與此同時(shí), 你可以添加不止一個(gè) tsconfig.json 文件, 其中每一個(gè)都會(huì)作為項(xiàng)目的一部分被構(gòu)建.
這使得你可以在不使用多個(gè)不同項(xiàng)目的情況下為應(yīng)用的不同部分使用不同的配置.

當(dāng)項(xiàng)目中添加了 tsconfig.json 文件時(shí), 我們還禁用了項(xiàng)目屬性頁面.
也就是說所有配置的改變必須在 tsconfig.json 文件中進(jìn)行.
tsconfig.json 文件, 不在其上下文中的 TypeScript 文件不會(huì)被編譯.tsconfig.json 文件的限制, 而這個(gè)文件必須在根目錄或者 scripts 文件夾.tsconfig.json 的模板.