TypeScript 3.4 系列今天发布了最新维护版本 3.4.2,主要更改了 TSServer 以提供对 configurePlugin 请求的响应,并允许在项目创建期间由外部项目加载全局插件,这使得 IntelliCode 等 TSServer 插件可以在 Visual Studio 等编辑器中使用。

不过 TypeScript 3.4 作为一个较为重大的版本升级,我们有必要关注一下那些重要的新特性和改进。

TypeScript 3.4 正式版要新特性如下 • Faster subsequent builds with the --incremental flag 使用 --incremental flag 加快后续构建

• Higher order type inference from generic functions 泛型函数的高阶类型推导

• Improvements for ReadonlyArray and readonly tuples ReadonlyArray 和 readonly 元组的改进

• const assertions const 断言

• Type-checking for globalThis globalThis 类型检查

• Convert parameters to destructured object 将参数转换为析构对象

使用 --incremental flag 加快后续构建

TypeScript 3.4 引入了一个名为--incremental的新 flag,它会提醒 TypeScript 保存上一次编译中有关项目图的信息。这样下次 TypeScript 调用--incremental时,它将使用该信息以最低的成本来进行类型检查。

关于此特性,我们此前已经具体报导过,查看:引入--incrementalflag 以更快地构建后续版本。(https://www.oschina.net/news/105228/typescript-3-4-rc-released)

泛型函数的高阶类型推导

此版本在推导方面有一些改进,其中一大亮点是涉及从其它泛型函数推导类型的函数。

考虑以下片段:


function compose<A, B, C>(f: (arg: A) => B, g: (arg: B) => C): (arg: A) => C {    return x => g(f(x));}

compose 接受其它两个函数: • f 接受类型 A 的一些参数并返回类型 B 的值

• g 接受类型 f 返回的类型 B 的参数,并返回类型 C 的值

之后 compose 返回一个以 f 为参数的 g 函数,在调用此函数时,TypeScript 将尝试通过称为类型参数推导的方式计算出 A、B 和 C 的类型,这通常很有效:


interface Person {    name: string;    age: number;}function getDisplayName(p: Person) {    return p.name.toLowerCase();}function getLength(s: string) {    return s.length;}// has type '(p: Person) => number'const getDisplayNameLength = compose(    getDisplayName,    getLength,);// works and returns the type 'number'getDisplayNameLength({ name: "Person McPersonface", age: 42 });

但是当传递其它泛型函数时,像 compose 这样的泛型函数无法生效,比如:


interface Box<T> {    value: T;}function makeArray<T>(x: T): T[] {    return [x];}function makeBox<U>(value: U): Box<U> {    return { value };}// has type '(arg: {}) => Box<{}[]>'const makeBoxedArray = compose(    makeArray,    makeBox,)makeBoxedArray("hello!").value[0].toUpperCase();//                                ~~~~~~~~~~~// error: Property 'toUpperCase' does not exist on type '{}'.

这其中会出现推导出错。现在 TypeScript 3.4 在推导返回类型为函数的泛型函数的参数类型时,将根据需要将泛型函数参数中的类型参数传播到生成的函数类型中。也就是说,现在不生成:


(arg: {}) => Box<{}[]>

而是生成:


<T>(arg: T) => Box<T[]>

ReadonlyArray 和 readonly 元组的改进

TypeScript 3.4 中使用只读数组类型变得更加容易。

ReadonlyArray 类型描述了只能读取的数组,任何引用 ReadonlyArray 的变量都不能增或删,也不能在替换数组元素。

TypeScript 3.4 为 ReadonlyArray 引入了一种新的数组类型只读修饰符 readonly,简化了对数组只读的限定:


function foo(arr: readonly string[]) {    arr.slice();        // okay    arr.push("hello!"); // error!}

此外,可以使用 readonly 关键字为任何元组类型添加前缀,使其成为只读元组,就像上边说的可以使用数组简写语法一样:


function foo(pair: readonly [string, string]) {    console.log(pair[0]);   // okay    pair[1] = "hello!";     // error}

const 断言

TypeScript 3.4 引入了一个名为 const 断言的文字值结构,它的语法是一个类型断言,用 const 代替类型。

// Type '10'let x = 10 as const;// Type 'readonly [10, 20]'let y = [10, 20] as const;// Type '{ readonly text: "hello" }'let z = { text: "hello" } as const;

此功能意味着一般情况下可以省略原本仅用于提示编译器不可变性的类型:


// Works with no types referenced or declared.// We only needed a single const assertion.function getShapes() {    let result = [        { kind: "circle", radius: 100, },        { kind: "square", sideLength: 50, },    ] as const;        return result;}for (const shape of getShapes()) {    // Narrows perfectly!    if (shape.kind === "circle") {        console.log("Circle radius", shape.radius);    }    else {        console.log("Square side length", shape.sideLength);    }}

globalThis 类型检查

在全局范围内访问或声明值有时会非常困难,TypeScript 3.4 支持 ECMAScript 新全局变量 globalThis 类型检查。globalThis 提供了一种访问全局范围的标准方法,可以在不同的环境中使用。


// in a global file:var abc = 100;// Refers to 'abc' from above.globalThis.abc = 200;

用 let 和 const 声明的全局变量不会出现在 globalThis 上:


let answer = 42;// error! Property 'answer' does not exist on 'typeof globalThis'.globalThis.answer = 333333;

将参数转换为析构对象

有时参数列表会显得笨重,比如以下例子,调用者很容易混淆给定的参数顺序。:


function updateOptions(    hue?: number,    saturation?: number,    brightness?: number,    positionX?: number,    positionY?: number,    positionZ?: number,) {        // ....}

常见的 JavaScript 模式是使用“选项对象”,以便明确命名每个选项,并且顺序无关紧要。这模拟了其它语言称为“命名参数”的功能:


interface Options {    hue?: number,    saturation?: number,    brightness?: number,    positionX?: number,    positionY?: number,    positionZ?: number,}function updateOptions(options: Options = {}) {        // ....}

TypeScript 3.4 中实现了一种重构,可将现有函数转换为使用这种“命名参数”模式:

在存在多个参数的情况下,TypeScript 会提供重构以将参数列表转换为单个析构对象。

更多更具体的更新内容查看发布公告(https://devblogs.microsoft.com/typescript/announcing-typescript-3-4/)。

此外官方还透露了下一个版本 3.5 的一些关键亮点,可能会包括 JavaScript 项目的 .d.ts 文件,以及一些编辑器生产力功能。