TypeScript 2.4
动态导入表达式
动态 import 表达式是一个新特性,也是 ECMAScript 的一部分,允许用户在程序的任意点异步请求一个模块。
这意味着你可以有条件地、懒加载地导入其他模块和库。 例如,这里有一个 async 函数,只在需要时才导入一个实用工具库:
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 模块目标一起使用。
字符串枚举
TypeScript 2.4 现在允许枚举成员包含字符串初始化器。
enum Colors {
Red = "RED",
Green = "GREEN",
Blue = "BLUE"
}需要注意的是,使用字符串初始化的枚举无法反向映射以获取原始枚举成员名称。 换句话说,你不能写 Colors["RED"] 来获取字符串 "Red"。
泛型的改进推断
TypeScript 2.4 在泛型推断的方式上引入了一些很棒的变化。
返回类型作为推断目标
首先,TypeScript 现在可以对调用的返回类型进行推断。 这可以改善你的体验并捕获错误。 现在可以工作的示例:
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);作为一个你可能因此发现的新错误的例子:
let x: Promise<string> = new Promise(resolve => {
resolve(10);
// ~~ 错误!
});从上下文类型推断类型参数
在 TypeScript 2.4 之前,在以下示例中
let f: <T>(x: T) => T = y => y;y 的类型将是 any。 这意味着程序会通过类型检查,但从技术上讲,你可以对 y 做任何事情,例如:
let f: <T>(x: T) => T = y => y() + y.foo.bar;最后一个例子实际上不是类型安全的。
在 TypeScript 2.4 中,右侧的函数隐式获得类型参数,并且 y 被推断为具有该类型参数的类型。
如果你以类型参数约束不支持的方式使用 y,你将正确地得到一个错误。 在这种情况下,T 的约束是(隐式地){},因此最后一个例子将适当地失败。
对泛型函数的更严格检查
TypeScript 现在在比较两个单签名类型时尝试统一类型参数。 因此,在关联两个泛型签名时,你将获得更严格的检查,并可能捕获一些错误。
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; // 错误
b = a; // 可以
}回调参数的严格逆变
TypeScript 一直以来都是双变地比较参数。 这有很多原因,但总的来说,直到我们看到它对 Promise 和 Observable 的一些不利影响之前,这对我们的用户来说并不是一个大问题。
TypeScript 2.4 在关联两个回调类型时收紧了这一点。例如:
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 也会双向关联那些参数的类型。
在 TS 2.4 中关联 map 的类型时,语言会检查每个参数是否为回调类型,如果是,它将确保这些参数相对于当前关系以逆变的方式进行检查。
换句话说,TypeScript 现在捕获了上述错误,这可能对某些用户来说是破坏性更改,但在很大程度上是有帮助的。
弱类型检测
TypeScript 2.4 引入了“弱类型”的概念。 任何只包含一组全可选属性的类型都被认为是弱的。 例如,这个 Options 类型是一个弱类型:
interface Options {
data?: string;
timeout?: number;
maxRetries?: number;
}在 TypeScript 2.4 中,当属性没有重叠时,将任何值赋值给弱类型现在是一个错误。 例如:
function sendMessage(options: Options) {
// ...
}
const opts = {
payload: "hello world!",
retryOnFail: true
};
// 错误!
sendMessage(opts);
// 'opts' 的类型与 'Options' 本身没有重叠。
// 也许我们本意是使用 'data'/'maxRetries' 而不是 'payload'/'retryOnFail'。你可以认为这是 TypeScript“加强”了这些类型的弱保证,以捕获原本会静默的 bug。
由于这是一个破坏性更改,你可能需要了解解决方法,这些方法与严格对象字面量检查的解决方法相同:
- 如果属性确实存在,就声明它们。
- 为弱类型添加索引签名(即
[propName: string]: {})。 - 使用类型断言(即
opts as Options)。