TypeScript 2.2

支持混合类

¥Support for Mix-in classes

TypeScript 2.2 增加了对 ECMAScript 2015 mixin 类模式的支持(更多详情请参阅 MDN Mixin 描述“实际” 与 JavaScript 类的混合),以及在交叉类型中将 mixin 构造签名与常规构造签名相结合的规则。

¥TypeScript 2.2 adds support for the ECMAScript 2015 mixin class pattern (see MDN Mixin description and “Real” Mixins with JavaScript Classes for more details) as well as rules for combining mixin construct signatures with regular construct signatures in intersection types.

首先介绍一些术语

¥First some terminology

混合构造函数类型是指具有单个构造函数签名的类型,该签名包含一个 any[] 类型的剩余参数和一个类似对象的返回类型。例如,给定一个类似对象的类型 Xnew (...args: any[]) => X 是一个混合构造函数类型,其实例类型为 X

¥A mixin constructor type refers to a type that has a single construct signature with a single rest argument of type any[] and an object-like return type. For example, given an object-like type X, new (...args: any[]) => X is a mixin constructor type with an instance type X.

混合类是一种类声明或表达式,它 extends 是类型参数类型的表达式。以下规则适用于 mixin 类声明:

¥A mixin class is a class declaration or expression that extends an expression of a type parameter type. The following rules apply to mixin class declarations:

  • extends 表达式的类型参数类型必须限制为混合构造函数类型。

    ¥The type parameter type of the extends expression must be constrained to a mixin constructor type.

  • 混合类的构造函数(如果有)必须具有一个 any[] 类型的剩余参数,并且必须使用展开运算符将这些参数作为 super(...args) 调用中的参数传递。

    ¥The constructor of a mixin class (if any) must have a single rest parameter of type any[] and must use the spread operator to pass those parameters as arguments in a super(...args) call.

给定一个表达式 Base,其参数类型为 T,且具有约束 X,则混合类 class C extends Base {...} 将被处理为 Base 具有类型 X,并且结果类型为交叉 typeof C & T。换句话说,混合类表示为混合类构造函数类型和参数基类构造函数类型的交叉。

¥Given an expression Base of a parametric type T with a constraint X, a mixin class class C extends Base {...} is processed as if Base had type X and the resulting type is the intersection typeof C & T. In other words, a mixin class is represented as an intersection between the mixin class constructor type and the parametric base class constructor type.

当获取包含混合构造函数类型的交叉类型的构造函数签名时,混合构造函数签名将被丢弃,其实例类型将混合到交叉类型中其他构造函数签名的返回类型中。例如,交叉类型 { new(...args: any[]) => A } & { new(s: string) => B } 具有单个构造签名 new(s: string) => A & B

¥When obtaining the construct signatures of an intersection type that contains mixin constructor types, the mixin construct signatures are discarded and their instance types are mixed into the return types of the other construct signatures in the intersection type. For example, the intersection type { new(...args: any[]) => A } & { new(s: string) => B } has a single construct signature new(s: string) => A & B.

将以上所有规则整合到一个示例中

¥Putting all of the above rules together in an example

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;

Mixin 类可以通过在类型参数的约束中指定构造签名返回类型来限制它们可以混合到的类的类型。例如,以下 WithLocation 函数实现了一个子类工厂,它将 getLocation 方法添加到任何满足 Point 接口的类(即具有 number 类型属性的 xy)。

¥Mixin classes can constrain the types of classes they can mix into by specifying a construct signature return type in the constraint for the type parameter. For example, the following WithLocation function implements a subclass factory that adds a getLocation method to any class that satisfies the Point interface (i.e. that has x and y properties of type number).

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 类型

¥object type

TypeScript 没有表示非原始类型的类型,即任何非 numberstringbooleansymbolnullundefined 的类型。输入新的 object 类型。

¥TypeScript did not have a type that represents the non-primitive type, i.e. any thing that is not number, string, boolean, symbol, null, or undefined. Enter the new object type.

使用 object 类型,可以更好地表示像 Object.create 这样的 API。例如:

¥With object type, APIs like Object.create can be better represented. For example:

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

¥Support for new.target

new.target 元属性是 ES2015 中引入的新语法。当通过 new 创建构造函数实例时,new.target 的值将被设置为最初用于分配实例的构造函数的引用。如果函数是通过 new 调用而不是构造的,则 new.target 会被设置为 undefined

¥The new.target meta-property is new syntax introduced in ES2015. When an instance of a constructor is created via new, the value of new.target is set to be a reference to the constructor function initially used to allocate the instance. If a function is called rather than constructed via new, new.target is set to undefined.

当需要在类构造函数中设置 Object.setPrototypeOf__proto__ 时,new.target 非常方便。一个这样的用例是在 NodeJS v4 及更高版本中继承 Error

¥new.target comes in handy when Object.setPrototypeOf or __proto__ needs to be set in a class constructor. One such use case is inheriting from Error in NodeJS v4 and higher.

示例

¥Example

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 代码如下

¥This results in the generated 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 在编写可构造函数时也非常有用,例如:

¥new.target also comes in handy for writing constructable functions, for example:

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

翻译为:

¥Which translates to:

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

更好地检查表达式操作数中的 null/undefined

¥Better checking for null/undefined in operands of expressions

TypeScript 2.2 改进了表达式中可空操作数的检查。具体来说,以下情况现在会被标记为错误:

¥TypeScript 2.2 improves checking of nullable operands in expressions. Specifically, these are now flagged as errors:

  • 如果 + 运算符的任一操作数可空,且两个操作数均不属于 anystring 类型。

    ¥If either operand of a + operator is nullable, and neither operand is of type any or string.

  • 如果 -***/%<<>>>>>&|^ 运算符的任一操作数可空。

    ¥If either operand of a -, *, **, /, %, <<, >>, >>>, &, |, or ^ operator is nullable.

  • 如果 <><=>=in 运算符的任一操作数可空。

    ¥If either operand of a <, >, <=, >=, or in operator is nullable.

  • 如果 instanceof 运算符的右操作数可空。

    ¥If the right operand of an instanceof operator is nullable.

  • 如果 +-~++-- 一元运算符的操作数可为空。

    ¥If the operand of a +, -, ~, ++, or -- unary operator is nullable.

如果操作数的类型为 nullundefined,或者为包含 nullundefined 的联合类型,则该操作数被视为可空。请注意,联合类型情况仅发生在 strictNullChecks 模式下,因为在经典类型检查模式下,nullundefined 会从联合中消失。

¥An operand is considered nullable if the type of the operand is null or undefined or a union type that includes null or undefined. Note that the union type case only occurs in strictNullChecks mode because null and undefined disappear from unions in classic type checking mode.

带有字符串索引签名的类型的点属性

¥Dotted property for types with string index signatures

带有字符串索引签名的类型可以使用 [] 表示法进行索引,但不允许使用 . 表示法。从 TypeScript 2.2 开始,应该允许使用其中任何一种。

¥Types with a string index signature can be indexed using the [] notation, but were not allowed to use the .. Starting with TypeScript 2.2 using either should be allowed.

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

这仅适用于具有显式字符串索引签名的类型。使用 . 表示法访问类型的未知属性仍然是错误的。

¥This only applies to types with an explicit string index signature. It is still an error to access unknown properties on a type using . notation.

支持 JSX 元素子元素上的展开运算符

¥Support for spread operator on JSX element children

TypeScript 2.2 增加了对 JSX 元素子元素使用展开的支持。有关更多详细信息,请参阅 facebook/jsx#57

¥TypeScript 2.2 adds support for using spread on JSX element children. Please see facebook/jsx#57 for more details.

示例

¥Example

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

¥New jsx: react-native

React-native 构建管道要求所有文件都具有 .js 扩展名,即使文件包含 JSX 语法也是如此。新的 jsxreact-native 将在输出文件中保留 JSX 语法,但赋予其 .js 扩展名。

¥React-native build pipeline expects all files to have a .js extension even if the file contains JSX syntax. The new jsx value react-native will preserve the JSX syntax in the output file, but give it a .js extension.