TypeScript 2.2

混入(Mix-in)類支援

TypeScript 2.2 增加了對 ECMAScript 2015 混入類模式的支援(更多詳情請參閱 MDN 混入描述“Real” Mixins with JavaScript Classes),以及在交叉型別中結合混入構造簽名與常規構造簽名的規則。

首先是一些術語

混入建構函式型別(mixin constructor type)是指具有單個構造簽名、且該簽名包含一個 any[] 型別的剩餘引數(rest argument)並返回物件型別的一種型別。例如,對於一個物件型別 Xnew (...args: any[]) => X 就是一個例項型別為 X 的混入建構函式型別。

混入類(mixin class)extends 一個型別引數表示式的類宣告或表示式。混入類宣告適用以下規則:

  • extends 表示式中的型別引數型別必須約束為混入建構函式型別。
  • 混入類的建構函式(如果有)必須包含一個 any[] 型別的剩餘引數,並且必須使用展開運算子(spread operator)在 super(...args) 呼叫中傳遞這些引數。

給定一個具有約束 X 的引數化型別 T 的表示式 Base,混入類 class C extends Base {...} 的處理方式就像 Base 具有型別 X 一樣,其生成的型別是交叉型別 typeof C & T。換句話說,混入類被表示為混入類建構函式型別與引數化基類建構函式型別之間的交集。

當獲取包含混入建構函式型別的交叉型別的構造簽名時,混入構造簽名會被丟棄,其例項型別會混合到交叉型別中其他構造簽名的返回型別中。例如,交叉型別 { new(...args: any[]) => A } & { new(s: string) => B } 只有一個構造簽名 new(s: string) => A & B

將以上所有規則彙總到一個示例中
ts
class Point {
constructor(public x: number, public y: number) {}
}
class Person {
constructor(public name: string) {}
}
type Constructor<T> = new (...args: any[]) => T;
function Tagged<T extends Constructor<{}>>(Base: T) {
return class extends Base {
_tag: string;
constructor(...args: any[]) {
super(...args);
this._tag = "";
}
};
}
const TaggedPoint = Tagged(Point);
let point = new TaggedPoint(10, 20);
point._tag = "hello";
class Customer extends Tagged(Person) {
accountBalance: number;
}
let customer = new Customer("Joe");
customer._tag = "test";
customer.accountBalance = 0;

混入類可以透過在型別引數的約束中指定構造簽名的返回型別,來限制它們可以混入的類型別。例如,下面的 WithLocation 函式實現了一個子類工廠,它為任何滿足 Point 介面(即具有 xy 屬性且型別為 number)的類添加了 getLocation 方法。

ts
interface Point {
x: number;
y: number;
}
const WithLocation = <T extends Constructor<Point>>(Base: T) =>
class extends Base {
getLocation(): [number, number] {
return [this.x, this.y];
}
};

object 型別

TypeScript 之前沒有一種型別可以代表非原始型別,即任何不是 numberstringbooleansymbolnullundefined 的型別。現在引入了新的 object 型別。

有了 object 型別,像 Object.create 這樣的 API 可以得到更好的表述。例如:

ts
declare function create(o: object | null): void;
create({ prop: 0 }); // OK
create(null); // OK
create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error

new.target 的支援

new.target 元屬性是 ES2015 引入的新語法。當透過 new 建立建構函式的例項時,new.target 的值被設定為對最初用於分配例項的建構函式的引用。如果函式是直接呼叫而非透過 new 構造,則 new.target 被設定為 undefined

當需要在類建構函式中設定 Object.setPrototypeOf__proto__ 時,new.target 非常有用。一個典型的用例是在 NodeJS v4 及更高版本中繼承 Error

示例
ts
class CustomError extends Error {
constructor(message?: string) {
super(message); // 'Error' breaks prototype chain here
Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain
}
}

這將生成如下 JS:

js
var CustomError = (function(_super) {
__extends(CustomError, _super);
function CustomError() {
var _newTarget = this.constructor;
var _this = _super.apply(this, arguments); // 'Error' breaks prototype chain here
_this.__proto__ = _newTarget.prototype; // restore prototype chain
return _this;
}
return CustomError;
})(Error);

new.target 在編寫可建構函式時也很有用,例如:

ts
function f() {
if (new.target) {
/* called via 'new' */
}
}

它會被翻譯為:

js
function f() {
var _newTarget = this && this instanceof f ? this.constructor : void 0;
if (_newTarget) {
/* called via 'new' */
}
}

更好地檢查表示式運算元中的 null/undefined

TypeScript 2.2 改進了表示式中可空運算元的檢查。具體來說,以下情況現在會被標記為錯誤:

  • 如果 + 運算子的任一運算元是可空的,且沒有運算元是 anystring 型別。
  • 如果 -, *, **, /, %, <<, >>, >>>, &, |, 或 ^ 運算子的任一運算元是可空的。
  • 如果 <, >, <=, >=, 或 in 運算子的任一運算元是可空的。
  • 如果 instanceof 運算子的右運算元是可空的。
  • 如果 +, -, ~, ++, 或 -- 一元運算子的運算元是可空的。

如果運算元的型別是 nullundefined,或者是包含 nullundefined 的聯合型別,則認為該運算元是可空的。請注意,聯合型別的情況僅發生在 strictNullChecks 模式下,因為在經典型別檢查模式下,nullundefined 會從聯合型別中消失。

針對具有字串索引簽名的型別的點屬性訪問

具有字串索引簽名的型別可以使用 [] 符號進行索引,但以前不允許使用 . 符號。從 TypeScript 2.2 開始,兩者都將被允許。

ts
interface StringMap<T> {
[x: string]: T;
}
const map: StringMap<number>;
map["prop1"] = 1;
map.prop2 = 2;

這僅適用於具有顯式字串索引簽名的型別。使用 . 符號訪問型別上的未知屬性仍然是一個錯誤。

支援在 JSX 元素子級上使用展開運算子

TypeScript 2.2 增加了在 JSX 元素子級上使用展開運算子的支援。詳情請參閱 facebook/jsx#57

示例
ts
function Todo(prop: { key: number; todo: string }) {
return <div>{prop.key.toString() + prop.todo}</div>;
}
function TodoList({ todos }: TodoListProps) {
return (
<div>{...todos.map(todo => <Todo key={todo.id} todo={todo.todo} />)}</div>
);
}
let x: TodoListProps;
<TodoList {...x} />;

新增 jsx: react-native

React-native 構建流程要求所有檔案都具有 .js 副檔名,即使檔案包含 JSX 語法。新的 jsxreact-native 將在輸出檔案中保留 JSX 語法,但會賦予其 .js 副檔名。

TypeScript 文件是一個開源專案。歡迎 提交 Pull Request 來幫助我們改進這些頁面 ❤

此頁面的貢獻者
MHMohamed Hegazy (53)
OTOrta Therox (12)
EIEugene Ilyin (1)
JBJack Bates (1)
EBEli Barzilay (1)
2+

最後更新:2026 年 3 月 27 日