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 做任何操作,例如以下操作:

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. 如果属性确实存在,请声明它们。
  2. 向弱类型(即 [propName: string]: {})添加索引签名。
  3. 使用类型断言(即 opts as Options)。