动态的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);
}
许多 bundlers 工具已经支持依照这些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);
// ~~ Error!
});
在 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 在比较两个单一签名的类型时会尝试统一类型参数。 因此,在涉及到两个泛型签名的时候会进行更严格的检查,这就可能发现一些 bugs。
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
}
TypeScript 一直是以双变(bivariant)的方式来比较参数。 这样做有很多原因,总体上来说这不会有什么大问题直到我们发现它应用在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 会检查是否每个参数都是回调类型,如果是的话,它会确保那些参数根据它所在的位置以抗变(contravariant)地方式进行检查。
换句话说,TypeScript 现在可以捕获上面的 bug,这对某些用户来说可能是一个破坏性改动,但却是非常帮助的。
TypeScript 2.4 引入了“弱类型”的概念。 任何只包含了可选属性的类型被当作是“weak”。 比如,下面的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'
因为这是一个破坏性改动,你可能想要知道一些解决方法:
- 确定属性存在时再声明
- 给弱类型增加索引签名(比如
[propName: string]: {}
) - 使用类型断言(比如
opts as Options
)