使用 --incremental
标志
¥Faster subsequent builds with the --incremental
flag
TypeScript 3.4 引入了一个名为 incremental
的新标志,它指示 TypeScript 保存上次编译时的项目图信息。下次使用 incremental
调用 TypeScript 时,它将使用该信息来检测成本最低的类型检查方法并将更改发送到你的项目。
¥TypeScript 3.4 introduces a new flag called incremental
which tells TypeScript to save information about the project graph from the last compilation.
The next time TypeScript is invoked with incremental
, it will use that information to detect the least costly way to type-check and emit changes to your project.
// tsconfig.json{" ": {" ": true," ": "./lib"}," ": ["./src"]}
默认情况下,使用这些设置,当我们运行 tsc
时,TypeScript 会在输出目录 (./lib
) 中查找名为 .tsbuildinfo
的文件。如果 ./lib/.tsbuildinfo
不存在,则会生成一个。但如果确实如此,tsc
将尝试使用该文件逐步进行类型检查并更新我们的输出文件。
¥By default with these settings, when we run tsc
, TypeScript will look for a file called .tsbuildinfo
in the output directory (./lib
).
If ./lib/.tsbuildinfo
doesn’t exist, it’ll be generated.
But if it does, tsc
will try to use that file to incrementally type-check and update our output files.
这些 .tsbuildinfo
文件可以安全删除,并且不会对我们的代码在运行时产生任何影响。 - 它们纯粹用于加快编译速度。我们还可以给它们命名任何我们想要的名称,并使用 tsBuildInfoFile
选项将它们放置在任何我们想要的位置。
¥These .tsbuildinfo
files can be safely deleted and don’t have any impact on our code at runtime - they’re purely used to make compilations faster.
We can also name them anything that we want, and place them anywhere we want using the tsBuildInfoFile
option.
// front-end.tsconfig.json{" ": {" ": true," ": "./buildcache/front-end"," ": "./lib"}," ": ["./src"]}
复合项目
¥Composite projects
复合项目(tsconfig.json
,composite
设置为 true
)的部分目的是可以逐步构建不同项目之间的引用。因此,复合项目将始终生成 .tsbuildinfo
文件。
¥Part of the intent with composite projects (tsconfig.json
s with composite
set to true
) is that references between different projects can be built incrementally.
As such, composite projects will always produce .tsbuildinfo
files.
outFile
使用 outFile
时,构建信息文件的名称将基于输出文件的名称。例如,如果我们的输出 JavaScript 文件是 ./output/foo.js
,那么在 incremental
标志下,TypeScript 将生成文件 ./output/foo.tsbuildinfo
。如上所述,这可以通过 tsBuildInfoFile
选项来控制。
¥When outFile
is used, the build information file’s name will be based on the output file’s name.
As an example, if our output JavaScript file is ./output/foo.js
, then under the incremental
flag, TypeScript will generate the file ./output/foo.tsbuildinfo
.
As above, this can be controlled with the tsBuildInfoFile
option.
泛型函数的高阶类型推断
¥Higher order type inference from generic functions
当从其他泛型函数推断出可用于推断的自由类型变量时,TypeScript 3.4 现在可以生成泛型函数类型。这意味着许多函数组合模式在 3.4 中现在可以更好地工作。
¥TypeScript 3.4 can now produce generic function types when inference from other generic functions produces free type variables for inferences. This means many function composition patterns now work better in 3.4.
为了更具体,让我们构建一些动机并考虑以下 compose
函数:
¥To get more specific, let’s build up some motivation and consider the following compose
function:
ts
function compose<A, B, C>(f: (arg: A) => B, g: (arg: B) => C): (arg: A) => C {return (x) => g(f(x));}
compose
接受另外两个函数:
¥compose
takes two other functions:
-
f
接受一些(类型为A
)的参数,并返回一个B
类型的值¥
f
which takes some argument (of typeA
) and returns a value of typeB
-
g
接受一个B
类型(返回的是f
类型)的参数,并返回一个C
类型的值¥
g
which takes an argument of typeB
(the typef
returned), and returns a value of typeC
compose
返回一个函数,该函数将其参数传递给 f
,然后传递给 g
。
¥compose
then returns a function which feeds its argument through f
and then g
.
调用此函数时,TypeScript 将尝试通过称为类型参数推断的过程来确定 A
、B
和 C
的类型。这个推断过程通常效果很好:
¥When calling this function, TypeScript will try to figure out the types of A
, B
, and C
through a process called type argument inference.
This inference process usually works pretty well:
ts
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 });
这里的推断过程相当简单,因为 getDisplayName
和 getLength
使用的类型易于引用。但是,在 TypeScript 3.3 及更早版本中,像 compose
这样的泛型函数在传递给其他泛型函数时工作得不太好。
¥The inference process is fairly straightforward here because getDisplayName
and getLength
use types that can easily be referenced.
However, in TypeScript 3.3 and earlier, generic functions like compose
didn’t work so well when passed other generic functions.
ts
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 在从其他类型变量(例如 T
和 U
)推断时,会推断出空对象类型 ({}
)。
¥In older versions, TypeScript would infer the empty object type ({}
) when inferring from other type variables like T
and U
.
在 TypeScript 3.4 中进行类型参数推断时,对于返回函数类型的泛型函数调用,TypeScript 会根据需要将泛型函数参数中的类型参数传播到结果函数类型。
¥During type argument inference in TypeScript 3.4, for a call to a generic function that returns a function type, TypeScript will, as appropriate, propagate type parameters from generic function arguments onto the resulting function type.
换句话说,它不会生成类型,
¥In other words, instead of producing the type
ts
(arg: {}) => Box<{}[]>
TypeScript 3.4 生成的类型
¥TypeScript 3.4 produces the type
ts
<T>(arg: T) => Box<T[]>
请注意,T
已从 makeArray
传播到结果类型的类型参数列表中。这意味着 compose
参数的泛型被保留了,我们的 makeBoxedArray
示例可以正常工作!
¥Notice that T
has been propagated from makeArray
into the resulting type’s type parameter list.
This means that genericity from compose
’s arguments has been preserved and our makeBoxedArray
sample will just work!
ts
interface Box<T> {value: T;}function makeArray<T>(x: T): T[] {return [x];}function makeBox<U>(value: U): Box<U> {return { value };}// has type '<T>(arg: T) => Box<T[]>'const makeBoxedArray = compose(makeArray, makeBox);// works with no problem!makeBoxedArray("hello!").value[0].toUpperCase();
更多详情,请参阅 在原始变更中阅读更多内容。
¥For more details, you can read more at the original change.
改进了 ReadonlyArray
和 readonly
元组
¥Improvements for ReadonlyArray
and readonly
tuples
TypeScript 3.4 使使用只读类数组类型变得更加容易。
¥TypeScript 3.4 makes it a little bit easier to use read-only array-like types.
ReadonlyArray
的新语法
¥A new syntax for ReadonlyArray
ReadonlyArray
类型描述了只能读取的 Array
。任何引用 ReadonlyArray
的变量都无法添加、删除或替换数组中的任何元素。
¥The ReadonlyArray
type describes Array
s that can only be read from.
Any variable with a reference to a ReadonlyArray
can’t add, remove, or replace any elements of the array.
ts
function foo(arr: ReadonlyArray<string>) {arr.slice(); // okayarr.push("hello!"); // error!}
虽然在不打算进行任何修改的情况下,使用 ReadonlyArray
而不是 Array
是一种很好的做法,但考虑到数组具有更简洁的语法,这通常很麻烦。具体来说,number[]
是 Array<number>
的简写版本,就像 Date[]
是 Array<Date>
的简写一样。
¥While it’s good practice to use ReadonlyArray
over Array
when no mutation is intended, it’s often been a pain given that arrays have a nicer syntax.
Specifically, number[]
is a shorthand version of Array<number>
, just as Date[]
is a shorthand for Array<Date>
.
TypeScript 3.4 为 ReadonlyArray
引入了一种新的语法,使用新的 readonly
修饰符修饰数组类型。
¥TypeScript 3.4 introduces a new syntax for ReadonlyArray
using a new readonly
modifier for array types.
ts
function foo(arr: readonly string[]) {arr.slice(); // okayarr.push("hello!"); // error!}
readonly
元组
¥readonly
tuples
TypeScript 3.4 还引入了对 readonly
元组的新支持。我们可以在任何元组类型前添加 readonly
关键字,使其成为 readonly
元组,就像我们现在使用数组简写语法一样。正如你所料,与可以写入位置的普通元组不同,readonly
元组只允许从这些位置读取。
¥TypeScript 3.4 also introduces new support for readonly
tuples.
We can prefix any tuple type with the readonly
keyword to make it a readonly
tuple, much like we now can with array shorthand syntax.
As you might expect, unlike ordinary tuples whose slots could be written to, readonly
tuples only permit reading from those positions.
ts
function foo(pair: readonly [string, string]) {console.log(pair[0]); // okaypair[1] = "hello!"; // error}
与普通元组是从 Array
扩展的类型相同。 - 一个包含 T1
、T2
等类型元素的元组Tn
扩展自 Array< T1 | T2 | …Tn >
- readonly
元组是从 ReadonlyArray
扩展而来的类型。readonly
元组包含元素 T1
、T2
、…Tn
扩展自 ReadonlyArray< T1 | T2 | …Tn
.
¥The same way that ordinary tuples are types that extend from Array
- a tuple with elements of type T1
, T2
, … Tn
extends from Array< T1 | T2 | … Tn >
- readonly
tuples are types that extend from ReadonlyArray
. So a readonly
tuple with elements T1
, T2
, … Tn
extends from ReadonlyArray< T1 | T2 | … Tn
.
readonly
映射类型修饰符和 readonly
数组
¥readonly
mapped type modifiers and readonly
arrays
在早期版本的 TypeScript 中,我们泛化了映射类型,以便对类似数组的类型进行不同的操作。这意味着像 Boxify
这样的映射类型可以同时作用于数组和元组。
¥In earlier versions of TypeScript, we generalized mapped types to operate differently on array-like types.
This meant that a mapped type like Boxify
could work on arrays and tuples alike.
ts
interface Box<T> {value: T;}type Boxify<T> = {[K in keyof T]: Box<T[K]>;};// { a: Box<string>, b: Box<number> }type A = Boxify<{ a: string; b: number }>;// Array<Box<number>>type B = Boxify<number[]>;// [Box<string>, Box<number>]type C = Boxify<[string, boolean]>;
遗憾的是,像 Readonly
工具类型这样的映射类型在数组和元组类型上实际上是无操作的。
¥Unfortunately, mapped types like the Readonly
utility type were effectively no-ops on array and tuple types.
ts
// lib.d.tstype Readonly<T> = {readonly [K in keyof T]: T[K];};// How code acted *before* TypeScript 3.4// { readonly a: string, readonly b: number }type A = Readonly<{ a: string; b: number }>;// number[]type B = Readonly<number[]>;// [string, boolean]type C = Readonly<[string, boolean]>;
在 TypeScript 3.4 中,映射类型中的 readonly
修饰符会自动将类似数组的类型转换为其对应的 readonly
类型。
¥In TypeScript 3.4, the readonly
modifier in a mapped type will automatically convert array-like types to their corresponding readonly
counterparts.
ts
// How code acts now *with* TypeScript 3.4// { readonly a: string, readonly b: number }type A = Readonly<{ a: string; b: number }>;// readonly number[]type B = Readonly<number[]>;// readonly [string, boolean]type C = Readonly<[string, boolean]>;
类似地,你可以编写一个类似 Writable
映射类型的实用类型,它剥离了 readonly
的特性,并将 readonly
数组容器转换回其可变的等效类型。
¥Similarly, you could write a utility type like Writable
mapped type that strips away readonly
-ness, and that would convert readonly
array containers back to their mutable equivalents.
ts
type Writable<T> = {-readonly [K in keyof T]: T[K];};// { a: string, b: number }type A = Writable<{readonly a: string;readonly b: number;}>;// number[]type B = Writable<readonly number[]>;// [string, boolean]type C = Writable<readonly [string, boolean]>;
警告
¥Caveats
尽管 readonly
类型修饰符如此出现,但它只能用于数组类型和元组类型的语法。它不是通用的类型运算符。
¥Despite its appearance, the readonly
type modifier can only be used for syntax on array types and tuple types.
It is not a general-purpose type operator.
ts
let err1: readonly Set<number>; // error!let err2: readonly Array<boolean>; // error!let okay: readonly boolean[]; // works fine
你可以 在拉取请求中查看更多详细信息。
¥You can see more details in the pull request.
const
断言
¥const
assertions
TypeScript 3.4 为字面值引入了一种名为 const
断言的新构造。它的语法是类型断言,用 const
代替类型名称(例如 123 as const
)。当我们使用 const
断言构造新的字面表达式时,我们可以向语言触发信号:
¥TypeScript 3.4 introduces a new construct for literal values called const
assertions.
Its syntax is a type assertion with const
in place of the type name (e.g. 123 as const
).
When we construct new literal expressions with const
assertions, we can signal to the language that
-
该表达式中的字面类型不应该被扩展(例如,不能从
"hello"
扩展到string
)。¥no literal types in that expression should be widened (e.g. no going from
"hello"
tostring
) -
对象字面量获取
readonly
属性¥object literals get
readonly
properties -
数组字面量变为
readonly
元组¥array literals become
readonly
tuples
ts
// Type '"hello"'let x = "hello" as const;// Type 'readonly [10, 20]'let y = [10, 20] as const;// Type '{ readonly text: "hello" }'let z = { text: "hello" } as const;
在 .tsx
文件之外,也可以使用尖括号断言语法。
¥Outside of .tsx
files, the angle bracket assertion syntax can also be used.
ts
// Type '"hello"'let x = <const>"hello";// Type 'readonly [10, 20]'let y = <const>[10, 20];// Type '{ readonly text: "hello" }'let z = <const>{ text: "hello" };
此功能意味着通常可以省略那些原本只是为了向编译器提示不可变性的类型。
¥This feature means that types that would otherwise be used just to hint immutability to the compiler can often be omitted.
ts
// 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);}}
请注意,以上内容无需类型注解。const
断言允许 TypeScript 采用表达式的最具体类型。
¥Notice the above needed no type annotations.
The const
assertion allowed TypeScript to take the most specific type of the expression.
如果你选择不使用 TypeScript 的 enum
结构,这甚至可以用于在纯 JavaScript 代码中启用类似 enum
的模式。
¥This can even be used to enable enum
-like patterns in plain JavaScript code if you choose not to use TypeScript’s enum
construct.
ts
export const Colors = {red: "RED",blue: "BLUE",green: "GREEN",} as const;// or use an 'export default'export default {red: "RED",blue: "BLUE",green: "GREEN",} as const;
警告
¥Caveats
需要注意的是,const
断言只能立即应用于简单的字面量表达式。
¥One thing to note is that const
assertions can only be applied immediately on simple literal expressions.
ts
// Error! A 'const' assertion can only be applied to a// to a string, number, boolean, array, or object literal.let a = (Math.random() < 0.5 ? 0 : 1) as const;let b = (60 * 60 * 1000) as const;// Works!let c = Math.random() < 0.5 ? (0 as const) : (1 as const);let d = 3_600_000 as const;
另一件需要记住的事情是,const
上下文不会立即将表达式转换为完全不可变的。
¥Another thing to keep in mind is that const
contexts don’t immediately convert an expression to be fully immutable.
ts
let arr = [1, 2, 3, 4];let foo = {name: "foo",contents: arr,} as const;foo.name = "bar"; // error!foo.contents = []; // error!foo.contents.push(5); // ...works!
更多详情,请参阅 查看相应的拉取请求。
¥For more details, you can check out the respective pull request.
globalThis
的类型检查
¥Type-checking for globalThis
TypeScript 3.4 引入了对 ECMAScript 的新 globalThis
类型检查的支持。 - 一个全局变量,它引用全局范围。与上述解决方案不同,globalThis
提供了一种访问全局范围的标准方法,该方法可在不同的环境中使用。
¥TypeScript 3.4 introduces support for type-checking ECMAScript’s new globalThis
- a global variable that, well, refers to the global scope.
Unlike the above solutions, globalThis
provides a standard way for accessing the global scope which can be used across different environments.
ts
// in a global file:var abc = 100;// Refers to 'abc' from above.globalThis.abc = 200;
请注意,使用 let
和 const
声明的全局变量不会显示在 globalThis
上。
¥Note that global variables declared with let
and const
don’t show up on globalThis
.
ts
let answer = 42;// error! Property 'answer' does not exist on 'typeof globalThis'.globalThis.answer = 333333;
还需要注意的是,TypeScript 在编译为旧版本的 ECMAScript 时不会将引用转换为 globalThis
。因此,除非你针对的是常青浏览器(已支持 globalThis
),否则你可能希望改用 使用合适的 polyfill。
¥It’s also important to note that TypeScript doesn’t transform references to globalThis
when compiling to older versions of ECMAScript.
As such, unless you’re targeting evergreen browsers (which already support globalThis
), you may want to use an appropriate polyfill instead.
更多实现详情,请参阅 该功能的拉取请求。
¥For more details on the implementation, see the feature’s pull request.