TypeScript 3.5

TypeScript 3.5

改进速度

TypeScript 3.5 为类型检查和增量构建采用了几个优化。

类型检查速度提升

TypeScript 3.5 包含对 TypeScript 3.4 的某些优化,可以更高效地进行类型检查。
在代码补全列表等类型检查驱动的操作上,这些改进效果显著。

改进 --incremental

TypeScript 3.5 通过缓存计算状态的信息(编译器设置、寻找文件的原因、文件在哪里被找到等等),改进了在 3.4 中的 --incremental 构建模式。我们发现重新构建花费的时间比 TypeScript 3.4 减少了 68%!

有关更多信息,你可以查看这些 pull requests

Omit 辅助类型

TypeScript 3.5 添加了新的 Omit 辅助类型,这个类型用来创建从原始类型中移除了某些属性的新类型。

type Person = {
  name: string;
  age: number;
  location: string;
};

type QuantumPerson = Omit;

// 相当于
type QuantumPerson = {
  name: string;
  age: number;
};

使用 Omit 辅助,我们有能力复制 Person 中除了 location 之外的所有属性。

有关更多细节,在 GitHub 查看添加 Omit 的 pull request, 以及有关剩余对象使用 Omit 的更改

改进了联合类型中多余属性的检查

在 TypeScript 3.4 及之前的版本中,会出现确实不应该存在的多余属性却被允许存在的情况。
例如,TypeScript 3.4 在对象字面量上允许不正确的 name 属性,甚至它的类型在 PointLabel 之中都不匹配。

type Point = {
  x: number;
  y: number;
};

type Label = {
  name: string;
};

const thing: Point | Label = {
  x: 0,
  y: 0,
  name: true // uh-oh!
};

以前,一个无区别的联合在它的成员上不会进行任何多余属性的检查,结果,类型错误的 name 属性溜了进来。

在 TypeScript 3.5 中,类型检查器至少会验证所有提供的属性属于某个联合类型的成员,且类型恰当,这意味着,上面的例子会正确的进行错误提示。

注意,只要属性类型有效,仍允许部分重叠。

const pl: Point | Label = {
  x: 0,
  y: 0,
  name: "origin" // okay
};

--allowUmdGlobalAccess 标志

在 TypeScript 3.5 中,使用新的 --allowUmdGlobalAccess 标志,你现在可以从任何位置引用全局的 UMD 申明——甚至模块。

export as namespace foo;

此模式增加了混合和匹配第三方库的灵活性,其中库声明的全局变量总是可以被使用,甚至可以从模块内部使用。

有关更多细节,查看 GitHub 上的 pull request

更智能的联合类型检查

在 TypeScript 3.4 以及之前的版本中,下面的例子会无效:

type S = { done: boolean, value: number }
type T =
  | { done: false, value: number }
  | { done: true, value: number };

declare let source: S;
declare let target: T;

target = source;

这是因为 S 无法被分配给 { done: false, value: number } 或者 { done: true, value: number }
为啥?
因为属性 doneS 不够具体——他是 boolean。而 T 的的每个成员有一个明确的为 true 或者 false 属性 done

这就是我们单独检查每个成员的意义:TypeScript 不只是将每个属性合并在一起,看看是否可以赋予 S

如果这样做,一些糟糕的代码可能会像下面这样:

interface Foo {
  kind: "foo";
  value: string;
}

interface Bar {
  kind: "bar";
  value: number;
}

function doSomething(x: Foo | Bar) {
  if (x.kind === "foo") {
    x.value.toLowerCase();
  }
}

// uh-oh - 幸运的是, TypeScript 在这里会提示错误!
doSomething({
  kind: "foo",
  value: 123,
});

然而,对于原始的例子,这有点过于严格。
如果你弄清除 S 的任何可能值的精确类型,你实际上可以看到它与 T 中的类型完全匹配。

在 TypeScript 3.5 中,当分配具有辨别属性的类型时,如 T,实际上进一步将类似 S 的类型分解为每个可能的成员类型的并集。
在这种情况下,由于 booleantruefalse 的联合,S 将被视为 {done:false,value:number}{done:true,value:number }

有关更多细节,你可以在 GitHub 上查看原始的 pull request

泛型构造函数的高阶类型推断

在 TypeScript 3.4 中,我们改进了对返回函数的泛型函数的推断:

function compose(f: (x: T) => U, g: (y: U) => V): (x: T) => V {
  return x => g(f(x))
}

将其他泛型函数作为参数,如下所示:

function arrayify(x: T): T[] {
  return [x];
}

type Box = { value: U }
function boxify(y: U): Box {
  return { value: y };
}

let newFn = compose(arrayify, boxify);

TypeScript 3.4 的推断允许 newFn 是泛型的。它的新类型是 <T>(x:T)=> Box <T []>。而不是旧版本推断的,相对无用的类型,如 (x:{})=> Box <{} []>

TypeScript 3.5 在处理构造函数的时候推广了这种行为。

class Box {
  kind: "box";
  value: T;
  constructor(value: T) {
    this.value = value;
  }
}

class Bag {
  kind: "bag";
  value: U;
  constructor(value: U) {
    this.value = value;
  }
}

function composeCtor(F: new (x: T) => U, G: new (y: U) => V): (x: T) => V {
  return x => new G(new F(x))
}

let f = composeCtor(Box, Bag); // 拥有类型 '(x: T) => Bag>'
let a = f(1024); // 拥有类型 'Bag>'

除了上面的组合模式之外,这种对泛型构造函数的新推断意味着在某些 UI 库(如 React )中对类组件进行操作的函数可以更正确地对泛型类组件进行操作。

type ComponentClass

= new (props: P) => Component

; declare class Component

{ props: P; constructor(props: P); } declare function myHoc

(C: ComponentClass

): ComponentClass

; type NestedProps = { foo: number, stuff: T }; declare class GenericComponent extends Component> { } // 类型为 'new (props: NestedProps) => Component>' const GenericComponent2 = myHoc(GenericComponent);

想学习更多,在 GitHub 上查看原始的 pull requet

参考

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 画漫画的程序员 GitHub仓库,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界