TypeScript 2.4

动态导入表达式

¥Dynamic Import Expressions

动态 import 表达式是 ECMAScript 的一项新功能,它允许用户在程序中的任意位置异步请求模块。

¥Dynamic import expressions are a new feature and part of ECMAScript that allows users to asynchronously request a module at any arbitrary point in your program.

这意味着你可以有条件地、延迟地导入其他模块和库。例如,这里有一个 async 函数,它仅在需要时导入一个工具库:

¥This means that you can conditionally and lazily import other modules and libraries. For example, here’s an async function that only imports a utility library when it’s needed:

ts
async function getZipFile(name: string, files: File[]): Promise<File> {
const zipUtil = await import("./utils/create-zip-file");
const zipContents = await zipUtil.getContentAsBlob(files);
return new File(zipContents, name);
}

许多打包器都支持根据这些 import 表达式自动拆分输出包,因此请考虑将此新功能与 esnext 模块目标一起使用。

¥Many bundlers have support for automatically splitting output bundles based on these import expressions, so consider using this new feature with the esnext module target.

字符串枚举

¥String Enums

TypeScript 2.4 现在允许枚举成员包含字符串初始化器。

¥TypeScript 2.4 now allows enum members to contain string initializers.

ts
enum Colors {
Red = "RED",
Green = "GREEN",
Blue = "BLUE"
}

需要注意的是,字符串初始化的枚举无法进行反向映射以获取原始枚举成员名称。换句话说,你不能写 Colors["RED"] 来获取字符串 "Red"

¥The caveat is that string-initialized enums can’t be reverse-mapped to get the original enum member name. In other words, you can’t write Colors["RED"] to get the string "Red".

改进了泛型的推断

¥Improved inference for generics

TypeScript 2.4 对泛型推断的方式进行了一些精彩的改进。

¥TypeScript 2.4 introduces a few wonderful changes around the way generics are inferred.

返回类型作为推断目标

¥Return types as inference targets

首先,TypeScript 现在可以推断调用的返回类型。这可以改善你的体验并捕获错误。现在可以正常工作的部分:

¥For one, TypeScript can now make inferences for the return type of a call. This can improve your experience and catch errors. Something that now works:

ts
function arrayMap<T, U>(f: (x: T) => U): (a: T[]) => U[] {
return a => a.map(f);
}
const lengths: (a: string[]) => number[] = arrayMap(s => s.length);

作为你可能因此发现的新错误的示例:

¥As an example of new errors you might spot as a result:

ts
let x: Promise<string> = new Promise(resolve => {
resolve(10);
// ~~ Error!
});

从上下文类型推断类型参数

¥Type parameter inference from contextual types

在 TypeScript 2.4 之前,以下示例中:

¥Prior to TypeScript 2.4, in the following example

ts
let f: <T>(x: T) => T = y => y;

y 的类型为 any。这意味着程序会进行类型检查,但从技术上讲,你可以使用 y 做任何事情,例如:

¥y would have the type any. This meant the program would type-check, but you could technically do anything with y, such as the following:

ts
let f: <T>(x: T) => T = y => y() + y.foo.bar;

最后一个例子实际上并非类型安全的。

¥That last example isn’t actually type-safe.

在 TypeScript 2.4 中,右侧的函数隐式地获得类型参数,并且 y 被推断为具有该类型参数的类型。

¥In TypeScript 2.4, the function on the right side implicitly gains type parameters, and y is inferred to have the type of that type-parameter.

如果你以类型参数约束不支持的方式使用 y,则会正确收到错误。在这种情况下,T 的约束条件(隐式)是 {},因此最后一个例子将自然失败。

¥If you use y in a way that the type parameter’s constraint doesn’t support, you’ll correctly get an error. In this case, the constraint of T was (implicitly) {}, so the last example will appropriately fail.

更严格的泛型函数检查

¥Stricter checking for generic functions

TypeScript 现在会在比较两个单签名类型时尝试统一类型参数。因此,在关联两个泛型签名时,你将受到更严格的检查,并且可能会捕获一些错误。

¥TypeScript now tries to unify type parameters when comparing two single-signature types. As a result, you’ll get stricter checks when relating two generic signatures, and may catch some bugs.

ts
type A = <T, U>(x: T, y: U) => [T, U];
type B = <S>(x: S, y: S) => [S, S];
function f(a: A, b: B) {
a = b; // Error
b = a; // Ok
}

回调参数的严格逆变

¥Strict contravariance for callback parameters

TypeScript 始终以双变量方式比较参数。造成这种情况的原因有很多,但总的来说,这对我们的用户来说并不是什么大问题,直到我们看到它对 PromiseObservable 造成的一些负面影响。

¥TypeScript has always compared parameters in a bivariant way. There are a number of reasons for this, but by-and-large this was not been a huge issue for our users until we saw some of the adverse effects it had with Promises and Observables.

TypeScript 2.4 在关联两个回调类型时对此进行了改进。例如:

¥TypeScript 2.4 introduces tightens this up when relating two callback types. For example:

ts
interface Mappable<T> {
map<U>(f: (x: T) => U): Mappable<U>;
}
declare let a: Mappable<number>;
declare let b: Mappable<string | number>;
a = b;
b = a;

在 TypeScript 2.4 之前,此示例会成功。在关联 map 的类型时,TypeScript 会双向关联它们的参数(即 f 的类型)。在关联每个 f 时,TypeScript 也会双向关联这些参数的类型。

¥Prior to TypeScript 2.4, this example would succeed. When relating the types of map, TypeScript would bidirectionally relate their parameters (i.e. the type of f). When relating each f, TypeScript would also bidirectionally relate the type of those parameters.

在 TS 2.4 中关联 map 的类型时,该语言会检查每个参数是否为回调类型,如果是,则会确保以与当前关系逆变的方式检查这些参数。

¥When relating the type of map in TS 2.4, the language will check whether each parameter is a callback type, and if so, it will ensure that those parameters are checked in a contravariant manner with respect to the current relation.

换句话说,TypeScript 现在可以捕获上述错误,这对某些用户来说可能是一个重大更改,但在很大程度上会有所帮助。

¥In other words, TypeScript now catches the above bug, which may be a breaking change for some users, but will largely be helpful.

弱类型检测

¥Weak Type Detection

TypeScript 2.4 引入了 “弱类型” 的概念。任何只包含一组全可选属性的类型都被认为是弱类型。例如,这个 Options 类型是一个弱类型:

¥TypeScript 2.4 introduces the concept of “weak types”. Any type that contains nothing but a set of all-optional properties is considered to be weak. For example, this Options type is a weak type:

ts
interface Options {
data?: string;
timeout?: number;
maxRetries?: number;
}

在 TypeScript 2.4 中,当属性没有重叠时,将任何内容赋值给弱类型现在都是错误的。例如:

¥In TypeScript 2.4, it’s now an error to assign anything to a weak type when there’s no overlap in properties. For example:

ts
function sendMessage(options: Options) {
// ...
}
const opts = {
payload: "hello world!",
retryOnFail: true
};
// Error!
sendMessage(opts);
// No overlap between the type of 'opts' and 'Options' itself.
// Maybe we meant to use 'data'/'maxRetries' instead of 'payload'/'retryOnFail'.

你可以将其视为 TypeScript “强化” 对这些类型的弱保证,以捕获原本可能隐藏的错误。

¥You can think of this as TypeScript “toughening up” the weak guarantees of these types to catch what would otherwise be silent bugs.

由于这是一项重大变更,你可能需要了解与严格对象字面量检查相同的解决方法:

¥Since this is a breaking change, you may need to know about the workarounds which are the same as those for strict object literal checks:

  1. 如果属性确实存在,请声明它们。

    ¥Declare the properties if they really do exist.

  2. 为弱类型(即 [propName: string]: {})添加索引签名。

    ¥Add an index signature to the weak type (i.e. [propName: string]: {}).

  3. 使用类型断言(例如 opts as Options)。

    ¥Use a type assertion (i.e. opts as Options).