TypeScript 4.2

Smarter Type Alias Preservation

TypeScript通过类型别名(type alias)来给类型声明新的名字。如果你正在编写一系列的函数,都使用了 string | number | boolean 类型,那么可以声明一个类型别名来避免重复。

  1. type BasicPrimitive = string | number | boolean;

当重用类型别名时,TypeScript已经使用了一组规则和推测来显示类型。例如以下代码片断:

  1. export type BasicPrimitive = number | string | boolean;
  2. export function doStuff(value: BasicPrimitive) {
  3. let x = value;
  4. return x;
  5. }

当使用Visual Studio、Visual Studio Code或者是TypeScript Playground编辑器开发时,如果鼠标悬停在 x 上,可以看到一个快捷信息栏中显示类型 BasicPrimitive。同样地,如果生成该文件的类型声明文件(输出 .d.ts 文件),其中 doStuff 函数会返回 BasicPrimitive 类型。

但是,如果返回一个 BasicPrimitiveundefined,会发生什么呢?

  1. export type BasicPrimitive = number | string | boolean;
  2. export function doStuff(value: BasicPrimitive) {
  3. if (Math.random() < 0.5) {
  4. return undefined;
  5. }
  6. return value;
  7. }

我们可以看看在TypeScript playground中会发生什么。TypeScript不会如我们期望的那样显示 doStuff 的返回类型为 BasicPrimitive | undefined,而是显示 string | number | boolean | undefined!为什么会这样?

这涉及到TypeScrpt在内部是如何表示类型的。当通过一个或多个联合类型(union type)来创建一个新的联合类型时,TypeScript将规范化normalize)这些类型成一个新的、扁平化的联合类型,从而导致丢失了类型信息。类型检查器将会从 string | number | boolean | undefined 来查找每一个类型组合来查看哪些类型别名可能被使用了,甚至可能存在多个 string | number | boolean 的类型别名。

在TypeScript 4.2,内部实现将更加智能一些。通过始终保存类型编写和创建的信息来追踪类型如何创建的。也追踪和区分了类型别名和这些别名的实例(instances)。

能够按照TypeScript用户编写代码时的意图来实现类型,可以避免某些超长显示的类型,从而使得生成的 d.ts 文件、错误信息以及编辑器中的类型显示可读性更好。这使得TypeScript对初学者来说更加友好。

具体细节可以参考:
the first pull request that improves various cases around preserving union type aliases
a second pull request that preserves indirect aliases.

Leading/Middle Rest Elements in Tuple Types

在TypeScript中,元组类型(tuple types)是一个有指定长度和元素类型的模型数组。

  1. // A tuple that stores a pair of numbers
  2. let a: [number, number] = [1, 2];
  3. // A tuple that stores a string, a number, and a boolean
  4. let b: [string, number, boolean] = ["hello", 42, true];

随着时间的推移,TypeScript的元组类型已经变得越来越复杂,因为他们也被用来模型化JavaScript,例如参数列表(parameter lists)。结果,可选元素类型(optional element),可变长元素类型(rest elements)以及用来改进工具性和可读性的类型标签(labels)也被引入。

  1. // A tuple that has either one or two strings.
  2. let c: [string, string?] = ["hello"];
  3. c = ["hello", "world"];
  4. // A labeled tuple that has either one or two strings.
  5. let d: [first: string, second?: string] = ["hello"];
  6. d = ["hello", "world"];
  7. // A tuple with a *rest element* - holds at least 2 strings at the front,
  8. // and any number of booleans at the back.
  9. let e: [string, string, ...boolean[]];
  10. e = ["hello", "world"];
  11. e = ["hello", "world", false];
  12. e = ["hello", "world", true, false, true];

在TypeScript 4.2中,可变长元素类型的使用得到的扩展。在之前的版本中,TypeScript只允许 ...rest 元素出现在元组类型的最后位置。

而现在,可变长元素类型可以出现在元组类型的任何位置,只需要遵循少量的限制。

  1. let foo: [...string[], number];
  2. foo = [123];
  3. foo = ["hello", 123];
  4. foo = ["hello!", "hello!", "hello!", 123];
  5. let bar: [boolean, ...string[], boolean];
  6. bar = [true, false];
  7. bar = [true, "some text", false];
  8. bar = [true, "some", "separated", "text", false];

唯一的限制是可变长元素类型可以出现在元组类型的任意位置,但是它的后面不可以再出现可选元素类型或者可变长元素类型。换句话说,每个元组只可以有一个可变长元素类型,并且可变长元素类型后面不可以再有可选元素类型。

  1. interface Clown { /*...*/ }
  2. interface Joker { /*...*/ }
  3. let StealersWheel: [...Clown[], "me", ...Joker[]];
  4. // ~~~~~~~~~~ Error!
  5. // A rest element cannot follow another rest element.
  6. let StringsAndMaybeBoolean: [...string[], boolean?];
  7. // ~~~~~~~~ Error!
  8. // An optional element cannot follow a rest element.

这些不是放在结尾处的可变长元素类型可以用来模型化接受任意数量头部参数再紧接着固定数量参数结尾的函数。

  1. declare function doStuff(...args: [...names: string[], shouldCapitalize: boolean]): void;
  2. doStuff(/*shouldCapitalize:*/ false)
  3. doStuff("fee", "fi", "fo", "fum", /*shouldCapitalize:*/ true);

即使JavaScript没有定义任何语法来模型化头部可变长参数,我们仍然可以声明 doStuff 函数,通过声明 ...args 作为可变长参数,其类型为一个头部使用了可变长元素类型的元组,使得该函数可以接受头部参数。这有助于模型化大量已存在的JavaScript函数!

具体细节,参考原始的pull request.

Stricter Checks For The in Operator

[--noPropertyAccessFromIndexSignature](https://devblogs.microsoft.com/typescript/announcing-typescript-4-2/#no-property-access-index-signature)

abstract Construct Signatures

The --explainFiles Flag

Improved Uncalled Function Checks in Logical Expressions

Destructured Variables Can Be Explicitly Marked as Unused

Relaxed Rules Between Optional Properties and String Index Signatures

Declare Missing Helper Function

Breaking Changes

TypeScript 4.1

1 Recursive Conditional Types

2 undefined in index signatures

3 --noImplicitOverride

Typescript 4.0

Variadic Tuple Types

解决“death by a thousand overloads”问题:
例如concat类型声明:

  1. function concat(arr1: [], arr2: []): [];
  2. function concat<A>(arr1: [A], arr2: []): [A];
  3. function concat<A, B>(arr1: [A, B], arr2: []): [A, B];
  4. function concat<A, B, C>(arr1: [A, B, C], arr2: []): [A, B, C];
  5. function concat<A, B, C, D>(arr1: [A, B, C, D], arr2: []): [A, B, C, D];
  6. function concat<A, B, C, D, E>(arr1: [A, B, C, D, E], arr2: []): [A, B, C, D, E];
  7. function concat<A, B, C, D, E, F>(arr1: [A, B, C, D, E, F], arr2: []): [A, B, C, D, E, F];)
  8. function concat<A2>(arr1: [], arr2: [A2]): [A2];
  9. function concat<A1, A2>(arr1: [A1], arr2: [A2]): [A1, A2];
  10. function concat<A1, B1, A2>(arr1: [A1, B1], arr2: [A2]): [A1, B1, A2];
  11. function concat<A1, B1, C1, A2>(arr1: [A1, B1, C1], arr2: [A2]): [A1, B1, C1, A2];
  12. function concat<A1, B1, C1, D1, A2>(arr1: [A1, B1, C1, D1], arr2: [A2]): [A1, B1, C1, D1, A2];
  13. function concat<A1, B1, C1, D1, E1, A2>(arr1: [A1, B1, C1, D1, E1], arr2: [A2]): [A1, B1, C1, D1, E1, A2];
  14. function concat<A1, B1, C1, D1, E1, F1, A2>(arr1: [A1, B1, C1, D1, E1, F1], arr2: [A2]): [A1, B1, C1, D1, E1, F1, A2];
  15. // ...
  • tuple类型的spread可以泛型化

    1. function tail<T extends any[]>(arr: readonly [any, ...T]) {
    2. const [_ignored, ...rest] = arr;
    3. return rest;
    4. }
  • rest元素可以出现在tuple的任意位置 ```typescript type Strings = [string, string]; type Numbers = [number, number];

type StrStrNumNumBool = […Strings, …Numbers, boolean]; // ^ = type StrStrNumNumBool = [string, string, number, number, boolean]

  1. 之前版本的TypeScript会报错:
  2. > A rest element must be last in a tuple type.
  3. 注意,spread的类型长度未知,导出的类型也是未知长度,且后续的类型会被合并进rest元素的类型
  4. ```typescript
  5. type Strings = [string, string];
  6. type Numbers = number[];
  7. type Unbounded = [...Strings, ...Numbers, boolean];
  8. // ^ = type Unbounded = [string, string, ...(number | boolean)[]]

现在可以为concat重新声明完美类型化的方法签名:

  1. type Arr = readonly any[];
  2. function concat<T extends Arr, U extends Arr>(arr1: T, arr2: U): [...T, ...U] {
  3. return [...arr1, ...arr2];
  4. }

更复杂的场景:

  1. function partialCall(f, ...headArgs) {
  2. return (...tailArgs) => f(...headArgs, ...tailArgs);
  3. }
  1. type Arr = readonly unknown[];
  2. function partialCall<T extends Arr, U extends Arr, R>(
  3. f: (...args: [...T, ...U]) => R,
  4. ...headArgs: T
  5. ) {
  6. return (...tailArgs: U) => f(...headArgs, ...tailArgs);
  7. }

使用时:

  1. const foo = (x: string, y: number, z: boolean) => {};
  2. const f1 = partialCall(foo, 100);
  3. # Argument of type 'number' is not assignable to parameter of type 'string'.
  4. const f2 = partialCall(foo, "hello", 100, true, "oops");
  5. # Expected 4 arguments, but got 5.
  6. // This works!
  7. const f3 = partialCall(foo, "hello");
  8. // ^ = const f3: (y: number, z: boolean) => void
  9. // What can we do with f3 now?
  10. // Works!
  11. f3(123, true);
  12. f3();
  13. # Expected 2 arguments, but got 0.
  14. f3(123, "hello");
  15. # Argument of type 'string' is not assignable to parameter of type 'boolean'.

Labeled Tuple Elements

  1. type Range = [start: number, end: number];
  2. type Foo = [first: number, second?: string, ...rest: any[]];
  3. type Bar = [first: string, number];
  4. # Tuple members must all have names or all not have names.

仅用于文档和开发工具,如编辑器支持,显示重载签名时提高可读性:
signatureHelpLabeledTuples.gif

2.4.3 Class Property Inference from Constructors

启用noImplicitAny时,TS 4.0可以利用控制流分析(control flow analysis)来确定class的属性类型:

  1. class Square {
  2. // Previously both of these were any
  3. area;
  4. // ^ = (property) Square.area: number
  5. sideLength;
  6. // ^ = (property) Square.sideLength: number
  7. constructor(sideLength: number) {
  8. this.sideLength = sideLength;
  9. this.area = sideLength ** 2;
  10. }
  11. }
  1. class Square {
  2. sideLength;
  3. // ^ = (property) Square.sideLength: number | undefined
  4. constructor(sideLength: number) {
  5. if (Math.random()) {
  6. this.sideLength = sideLength;
  7. }
  8. }
  9. get area() {
  10. return this.sideLength ** 2;
  11. # Object is possibly 'undefined'.
  12. }
  13. }

启用strictPropertyInitialization时,即使使用了确定赋值断言(definite assignment assertion (!)),依然需要显示的类型注释:

  1. class Square {
  2. // definite assignment assertion
  3. // v
  4. sideLength!: number;
  5. // type annotation
  6. constructor(sideLength: number) {
  7. this.initialize(sideLength);
  8. }
  9. initialize(sideLength: number) {
  10. this.sideLength = sideLength;
  11. }
  12. get area() {
  13. return this.sideLength ** 2;
  14. }
  15. }

2.4.4 Short-Circuiting Assigment Operators

logical and (&&), logical or (||), and nullish coalescing (??)

  1. if (!a) {
  2. a = b;
  3. }
  4. // or
  5. a || (a = b);
  6. // after
  7. a ||= b;
  1. let values: string[];
  2. (values ?? (values = [])).push("hello");
  3. // After
  4. (values ??= []).push("hello");

2.4.5 unknown on catch Clause Bindings

之前版本,catch 语句捕获的异常类型总是被设定为any,这意味可以对异常值做任何操作

  1. try {
  2. // Do some work
  3. } catch (x) {
  4. // x has type 'any' - have fun!
  5. console.log(x.message);
  6. console.log(x.toUpperCase());
  7. x++;
  8. x.yadda.yadda.yadda();
  9. }

4.0可以指定为unknows,此时需要进行类型检查才能操作异常值

  1. try {
  2. // ...
  3. } catch (e: unknown) {
  4. // Can't access values on unknowns
  5. console.log(e.toUpperCase());
  6. # Object is of type 'unknown'.
  7. if (typeof e === "string") {
  8. // We've narrowed 'e' down to the type 'string'.
  9. console.log(e.toUpperCase());
  10. }
  11. }
  • 默认catch变量的类型没有变更,未来会考虑引入一个新的模式flag--strict
  • 目前可以使用lint规则来强制要求catch变量必须显示地标注类型为:any或者:unknown

    2.4.6 Custom JSX Factories

    tsconfig.json中可以指定转换JSX时,使用的工厂方法:
    1. {
    2. compilerOptions: {
    3. target: "esnext",
    4. module: "commonjs",
    5. jsx: "react",
    6. jsxFactory: "h",
    7. jsxFragmentFactory: "Fragment",
    8. },
    9. }
    ```jsx // Note: these pragma comments need to be written // with a JSDoc-style multiline syntax to take effect.

/ @jsx h */ / @jsxFrag Fragment */

import { h, Fragment } from “preact”;

export const Header = ( <>

Welcome

</> );

  1. ```javascript
  2. // Note: these pragma comments need to be written
  3. // with a JSDoc-style multiline syntax to take effect.
  4. /** @jsx h */
  5. /** @jsxFrag Fragment */
  6. import { h, Fragment } from "preact";
  7. export const Header = (h(Fragment, null,
  8. h("h1", null, "Welcome")));

2.4.7 Speed Improvements in build mode witch --noEmitOnError

2.4.8 --incremental with --noEmit

现在可以同时使用--noEmit--incremental flag来加快构建。

2.4.9 Editor Improvements

Convert to Optional Chaining

convertToOptionalChain-4-0.gif

/** @deprecated */ Support

image.png

Partial Semantic Mode at Startup

启用Partial Semantic模式,通过延缓一些语法分析来加快打开代码文件的速度。
https://devblogs.microsoft.com/typescript/wp-content/uploads/sites/11/2020/08/partialModeFast.mp4

Smarter Auto-Imports

通过package.json中的peerDependencies中列明的包来改进自动import
typescript.preferences.includePackageJsonAutoImports

2.4.10 Our New Website!

全新的TypeScript网站 The TypeScript website
image.png

2.4.11 Breaking Changes

lib.d.ts Changes

  • types of DOM
  • document.origin => self.origin

    Properties Overriding Accessors (and vice versa) is an Error

    之前只有使用了useDefineForClassFields设置时,父类子类的Accessor和Property相互重写(overriding)才会报错,现在4.0版本无论是否配置都会报错。 ```typescript class Base { get foo() { return 100; } set foo(value) { // … } }

class Derived extends Base { foo = 10;

‘foo’ is defined as an accessor in class ‘Base’, but is overridden here in ‘Derived’ as an instance property.

}

  1. ```typescript
  2. class Base {
  3. prop = 10;
  4. }
  5. class Derived extends Base {
  6. get prop() {
  7. # 'prop' is defined as a property in class 'Base', but is overridden here in 'Derived' as an accessor.
  8. return 100;
  9. }
  10. }Try

Operands for delete must be optional

  • strictNullChecks
  • anyunknownnever,or be optional(contains undefined) ```typescript interface Thing { prop: string; }

function f(x: Thing) { delete x.prop;

The operand of a ‘delete’ operator must be optional.

}

  1. <a name="VnpmR"></a>
  2. #### Usage of TypeScript’s Node Factory is Deprecated
  3. 4.0提供了一个新的AST Node工厂API,旧版本的工厂函数都被deprecate
  4. <a name="tVIp0"></a>
  5. # TypeScript 3.8
  6. <a name="QsJtq"></a>
  7. ## Type-Only Imports and Exports
  8. <a name="qIHkg"></a>
  9. ##### 代码
  10. ```typescript
  11. import type { SomeThing } from "./some-module.js";
  12. export type { SomeThing };

说明
  • import/export type在运行时会被擦除,也就是说import和export语句不会出现在tsc编译后的代码中
  • class在运行时(runtime)是一个值,而在编译时(compile-time)是一个类型,使用上有上下文不同
  1. import type { Component }, { Component as RComponent } from "react";
  2. interface ButtonProps {
  3. // ...
  4. }
  5. class Button extends Component<ButtonProps> {
  6. // ~~~~~~~~~
  7. // error! 'Component' only refers to a type, but is being used as a value here.
  8. # // replace with RComponent
  9. // ...
  10. }
  • Compiler flag importsNotUsedValues
    • remove 抛弃imports,将成为default
    • preserve 保留imports,同样会保留imports导致的副作用(side-effects)
    • error 保留imports,但有value import只当成类型使用时,报错!目的:防止意外的imports,有副作用的import会被显式引入

2.2.2 ECMAScript Private Fields

代码
  1. class Person {
  2. #name: string
  3. constructor(name: string) {
  4. this.#name = name;
  5. }
  6. greet() {
  7. console.log(`Hello, my name is ${this.#name}!`);
  8. }
  9. }
  10. let jeremy = new Person("Jeremy Bearimy");
  11. jeremy.#name
  12. // ~~~~~
  13. // Property '#name' is not accessible outside class 'Person'
  14. // because it has a private identifier.

说明

# private fields和private modifier的差别:
  • private field使用#开头,有时候被称为private names
  • 在包含他的class中,private field名称被唯一限定(scoped) ```typescript class C { foo = 10;

    cHelper() {

    1. return this.foo;

    } }

class D extends C { foo = 20;

  1. dHelper() {
  2. return this.foo;
  3. }

}

let instance = new D(); // ‘this.foo’ refers to the same property on each instance.

console.log(instance.cHelper()); // prints ‘20’

console.log(instance.dHelper()); // prints ‘20’

  1. ```typescript
  2. class C {
  3. #foo = 10;
  4. cHelper() {
  5. return this.#foo;
  6. }
  7. }
  8. class D extends C {
  9. #foo = 20;
  10. dHelper() {
  11. return this.#foo;
  12. }
  13. }
  14. let instance = new D();
  15. // 'this.#foo' refers to a different field within each class.
  16. # console.log(instance.cHelper()); // prints '10'
  17. # console.log(instance.dHelper()); // prints '20'
  • privatepublic不可以用于# private field
  • 在包含他的class外,private fields不可以被访问或者探知
  • private modifier是编译时的私有性检查, 会被完全擦除,运行时无效;private field是在class外是完全不可访问的,是运行时的私有性检查 ```typescript class C { private foo = 10; }

// This is an error at compile time, // but when TypeScript outputs .js files, // it’ll run fine and print ‘10’.

console.log(new C().foo); // prints ‘10’

// ~~~ // error! Property ‘foo’ is private and only accessible within class ‘C’.

// TypeScript allows this at compile-time // as a “work-around” to avoid the error.

console.log(new C()[“foo”]); // prints ‘10’

  1. ```typescript
  2. class C {
  3. #foo = 10;
  4. }
  5. # console.log(new C().#foo); // SyntaxError
  6. // ~~~~
  7. // TypeScript reports an error *and*
  8. // this won't work at runtime!
  9. # console.log(new C()["#foo"]); // prints undefined
  10. // ~~~~~~~~~~~~~~~
  11. // TypeScript reports an error under 'noImplicitAny',
  12. // and this prints 'undefined'.
  • TypeScript只在ES6及更高的ECMScript Target支持# private field,因为底层实现依赖WeakMap来保证私有性;而private modifier可以用于所有的Target,甚至ES3。
  • 运行速度:# private field在底层实现中使用了WeakMap会影响性能,虽然一些Runtime可能会对# private field的实现进行优化,比如加速优化过的WeakMap,但并不是在所有Runtime中都会如此。
    示例
  1. private field访问导TypeError
  1. class Square {
  2. #sideLength: number;
  3. constructor(sideLength: number) {
  4. this.#sideLength = sideLength;
  5. }
  6. equals(other: any) {
  7. return this.#sideLength === other.#sideLength;
  8. }
  9. }
  10. const a = new Square(100);
  11. const b = { sideLength: 100 };
  12. // Boom!
  13. // TypeError: attempted to get private field on non-instance
  14. // This fails because 'b' is not an instance of 'Square'.
  15. console.log(a.equals(b));
  1. 使用前声明,否则导致SyntaxError
  1. class C {
  2. // No declaration for '#foo'
  3. // :(
  4. constructor(foo: number) {
  5. // SyntaxError!
  6. // '#foo' needs to be declared before writing to it.
  7. this.#foo = foo;
  8. }
  9. }
  1. class C {
  2. /** @type {number} */
  3. #foo;
  4. constructor(foo: number) {
  5. // This works.
  6. this.#foo = foo;
  7. }
  8. }

2.2.3 export * as ns Syntax

该语法用于export imports的场景,对应特性在ES2020 Target。

目前的代码:

  1. import * as utilities from "./utilities.js";
  2. export { utilities };

TS 3.8版本:

  1. export * as utilities from "./utilities.js";

2.2.4 Top-Level await

解决await只能用函数体的问题:

  • 只能使用在module中,可以用 export {} 来保证
  • 只能使用在 target >= es2017 ,且 module 设置为 esnextsystem
  • TS 3.8开始支持 ES2020 作为moduletarget 选项

目前的代码:

  1. async function main() {
  2. const response = await fetch("...");
  3. const greeting = await response.text();
  4. console.log(greeting);
  5. }
  6. main().catch(e => console.error(e))

TS 3.8版本:

2.2.5 es2020作为targetmodule的选项

  • 支持ECMAScript 2020的特性

    • optional chaining
    • nullish coalescing
    • export * as ns
    • 动态import(...)
    • bigint字面量

      2.2.5 JSDoc Property Modifiers

  • TS 3.8通过 allowJs 支持JavaScript源文件

  • 通过设置 checkJS 或使用 // 注释,也支持对这些文件进行类型检查
  • 支持利用JSDoc来进行类型检查

    • @private
    • @public
    • @protected
    • @readonly

      2.2.6 Better Directory Watching on Linux and watchOptions

      引入watchOptions 来更好的配置文件监视(watch)策略,详细配置如下:
  • watchFile 设置单个文件的监视策略,可选值如下:

    • fixedPollingInterval 一秒钟内按固定的时间间隔来检查每个文件
    • priorityPollingInterval 一秒钟内检查每个文件数次,但使用启发式策略来降低某些类型文件的检查频率
    • dynamicPriorityPolling 使用一个动态队列,修改频率越低的文件检查频率越低
    • useFsEvents (默认值) 尝试使用操作系统或文件系统原生事件来监视文件变更
    • useFsEventsOnParentDirectory 尝试使用操作系统或文件系统原生事件来监听父文件夹的变更,该选项可以降低文件watcher的数量,但代价是牺牲精确度。
  • watchDirectory 设置当系统缺乏递归式文件监视功能时整个目录树的监视策略。 可选值如下:
    • fixedPollingInterval 一秒钟内按固定的时间间隔来检查每个文件
    • dynamicPriorityPolling 一秒钟内检查每个文件数次,但使用启发式策略来降低某些类型文件的检查频率
    • useFsEvents (默认值) 尝试使用操作系统原生事件来监视文件变更
  • fallbackPolling: 当使用文件系统事件是,该选项用来指定系统已经耗尽或者不支持原生文件watcher时的轮询策略
    • fixedPollingInterval
    • priorityPollingInterval
    • dynamicPriorityPolling
  • synchronousWatchDirectory 关闭文件夹的延时监控。
  1. {
  2. // Some typical compiler options
  3. "compilerOptions": {
  4. "target": "es2020",
  5. "moduleResolution": "node",
  6. // ...
  7. },
  8. // NEW: Options for file/directory watching
  9. "watchOptions": {
  10. // Use native file system events for files and directories
  11. "watchFile": "useFsEvents",
  12. "watchDirectory": "useFsEvents",
  13. // Poll files for updates more frequently
  14. // when they're updated a lot.
  15. "fallbackPolling": "dynamicPriority"
  16. }
  17. }

2.2.7 “Fast and Loose” Incremental Checking

compiler选项: assumeChangesOnlyAffectDirectDependencies

  1. fileA.ts <- fileB.ts <- fileC.ts <- fileD.ts
  • --watch 模式下,fileA变化将导致fileB,fileC,fileD都被重新进行检查
  • 启用 assumeChangesOnlyAffectDirectDependencies ,只用fileA和fileB被重新检查

    Typescript 3.7

    Optional Chaining ?.

  • 检查变量是否是 null undefined

    1. let x = foo?.bar.baz();
  1. let x = (foo === null || foo === undefined) ?
  2. undefined :
  3. foo.bar.baz();
  • But if foo.bar is null or undefined …..
  • && vs ?.: '', 0, NaN, false ```typescript // Before if (foo && foo.bar && foo.bar.baz) { // … }

// After-ish if (foo?.bar?.baz) { // … }

  1. <a name="Ih2jb"></a>
  2. ### 2 Nullish Coalescing `??`
  3. ```typescript
  4. let x = foo ?? bar();
  1. let x = (foo !== null && foo !== undefined) ?
  2. foo :
  3. bar();

3 Assertion Functions

  1. function yell(str) {
  2. assert(typeof str === "string");
  3. return str.toUppercase();
  4. // ~~~~~~~~~~~
  5. // error: Property 'toUppercase' does not exist on type 'string'.
  6. // Did you mean 'toUpperCase'?
  7. }
  • asserts condition
  1. function assert(condition: any, msg?: string): asserts condition {
  2. if (!condition) {
  3. throw new AssertionError(msg)
  4. }
  5. }
  • asserts type
  1. function assertIsString(val: any): asserts val is string {
  2. if (typeof val !== "string") {
  3. throw new AssertionError("Not a string!");
  4. }
  5. }
  • Similar to type predicate signatures

  • 4 Better Support for never - Returning Functions

    5 (More) Recursive Type Aliases

    6 --declaration and --allowJs

    7 The useDefineForClassFields

    8 Build-Free Editing with Project References

    9 Uncalled Function Checks

    2.1.10 ts-nocheck in TypeScript Files

    2.1.11 Semicolon Formatter Option

    2.1.12 Breaking Changes

    2.1.12.1 DOM Changes

    2.1.12.2 Function Truthy Checks

    2.1.12.3 Local and Imported Type Declarations Now Conflict

    2.1.12.4 API Changes

2.3 TypeScript 3.9

2.3.1 改进Promise相关的类型推导和Promise.all

TypeScript 3.7版本更新了Promise.allPromise.race等函数的声明,但是带来了一些问题,特别是当类型值混合了nullunefined时。

  1. interface Lion {
  2. roar(): void
  3. }
  4. interface Seal {
  5. singKissFromARose(): void
  6. }
  7. async function visitZoo(lionExhibit: Promise<Lion>, sealExhibit: Promise<Seal | undefined>) {
  8. let [lion, seal] = await Promise.all([lionExhibit, sealExhibit]);
  9. lion.roar(); // uh oh
  10. // ~~~~
  11. // Object is possibly 'undefined'.
  12. }
  1. interface PromiseConstructor {
  2. readonly prototype: Promise<any>;
  3. new <T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void): Promise<T>;
  4. all<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>, T10 | PromiseLike<T10>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>;
  5. all<T1, T2, T3, T4, T5, T6, T7, T8, T9>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>;
  6. all<T1, T2, T3, T4, T5, T6, T7, T8>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>;
  7. all<T1, T2, T3, T4, T5, T6, T7>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>]): Promise<[T1, T2, T3, T4, T5, T6, T7]>;
  8. all<T1, T2, T3, T4, T5, T6>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>]): Promise<[T1, T2, T3, T4, T5, T6]>;
  9. all<T1, T2, T3, T4, T5>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>]): Promise<[T1, T2, T3, T4, T5]>;
  10. all<T1, T2, T3, T4>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>]): Promise<[T1, T2, T3, T4]>;
  11. all<T1, T2, T3>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>]): Promise<[T1, T2, T3]>;
  12. all<T1, T2>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>]): Promise<[T1, T2]>;
  13. all<T>(values: readonly (T | PromiseLike<T>)[]): Promise<T[]>;
  14. // see: lib.es2015.iterable.d.ts
  15. // all<T>(values: Iterable<T | PromiseLike<T>>): Promise<T[]>;
  16. race<T>(values: readonly T[]): Promise<T extends PromiseLike<infer U> ? U : T>;
  17. // see: lib.es2015.iterable.d.ts
  18. // race<T>(values: Iterable<T>): Promise<T extends PromiseLike<infer U> ? U : T>;
  19. reject<T = never>(reason?: any): Promise<T>;
  20. resolve<T>(value: T | PromiseLike<T>): Promise<T>;
  21. resolve(): Promise<void>;
  22. }
  23. declare var Promise: PromiseConstructor;

2.3.2 awaited Type

被移出3.9版本

2.3.3 Speed Improvements

  • unions
  • intersections
  • conditional types
  • mapped types

2.3.4 // @ts-expect-error Comment

压制TypeScript报错