更智能的类型别名保存
¥Smarter Type Alias Preservation
TypeScript 有一种为类型声明新名称的方法,称为类型别名。如果你正在编写一组在 string | number | boolean
上运行的函数,你可以编写类型别名以避免一遍又一遍地重复。
¥TypeScript has a way to declare new names for types called type aliases.
If you’re writing a set of functions that all work on string | number | boolean
, you can write a type alias to avoid repeating yourself over and over again.
ts
type BasicPrimitive = number | string | boolean;
TypeScript 始终使用一组规则和猜测来决定何时在输出类型时重用类型别名。例如,以以下代码片段为例。
¥TypeScript has always used a set of rules and guesses for when to reuse type aliases when printing out types. For example, take the following code snippet.
ts
export type BasicPrimitive = number | string | boolean;export function doStuff(value: BasicPrimitive) {let x = value;return x;}
如果我们在 Visual Studio、Visual Studio Code 或 TypeScript 演练场 等编辑器中将鼠标悬停在 x
上,我们将获得一个显示类型 BasicPrimitive
的快速信息面板。同样,如果我们获取此文件的声明文件输出(.d.ts
输出),TypeScript 会说 doStuff
返回 BasicPrimitive
。
¥If we hover our mouse over x
in an editor like Visual Studio, Visual Studio Code, or the TypeScript Playground, we’ll get a quick info panel that shows the type BasicPrimitive
.
Likewise, if we get the declaration file output (.d.ts
output) for this file, TypeScript will say that doStuff
returns BasicPrimitive
.
但是,如果我们返回 BasicPrimitive
或 undefined
会发生什么?
¥However, what happens if we return a BasicPrimitive
or undefined
?
ts
export type BasicPrimitive = number | string | boolean;export function doStuff(value: BasicPrimitive) {if (Math.random() < 0.5) {return undefined;}return value;}
我们可以看看 在 TypeScript 4.1 测试环境中 会发生什么。虽然我们可能希望 TypeScript 将 doStuff
的返回类型显示为 BasicPrimitive | undefined
,但它却显示为 string | number | boolean | undefined
!怎么回事?
¥We can see what happens in the TypeScript 4.1 playground.
While we might want TypeScript to display the return type of doStuff
as BasicPrimitive | undefined
, it instead displays string | number | boolean | undefined
!
What gives?
这与 TypeScript 内部如何表示类型有关。当使用一个或多个联合类型创建联合类型时,它总是会将这些类型规范化为一个新的扁平化联合类型。 - 但这样做会丢失信息。类型检查器必须查找 string | number | boolean | undefined
中的所有类型组合才能查看可以使用哪些类型别名,即使这样,string | number | boolean
也可能有多个类型别名。
¥Well this has to do with how TypeScript represents types internally.
When creating a union type out of one or more union types, it will always normalize those types into a new flattened union type - but doing that loses information.
The type-checker would have to find every combination of types from string | number | boolean | undefined
to see what type aliases could have been used, and even then, there might be multiple type aliases to string | number | boolean
.
在 TypeScript 4.2 中,我们的内部机制更加智能。我们通过保留类型最初的编写和构造方式来跟踪类型的构造方式。我们还会跟踪并区分类型别名与其他别名的实例!
¥In TypeScript 4.2, our internals are a little smarter. We keep track of how types were constructed by keeping around parts of how they were originally written and constructed over time. We also keep track of, and differentiate, type aliases to instances of other aliases!
能够根据你在代码中使用类型的方式打印类型,这意味着作为 TypeScript 用户,你可以避免显示一些令人遗憾的庞大类型,这通常意味着获得更好的 .d.ts
文件输出、错误消息以及快速信息和签名帮助中的编辑器内类型显示。这可以让 TypeScript 对新手更容易上手。
¥Being able to print back the types based on how you used them in your code means that as a TypeScript user, you can avoid some unfortunately humongous types getting displayed, and that often translates to getting better .d.ts
file output, error messages, and in-editor type displays in quick info and signature help.
This can help TypeScript feel a little bit more approachable for newcomers.
更多信息,请查看 改进各种保留联合类型别名情况的第一个拉取请求 和 第二个拉取请求,用于保留间接别名。
¥For more information, check out the first pull request that improves various cases around preserving union type aliases, along with a second pull request that preserves indirect aliases.
元组类型中的前导/中间剩余元素
¥Leading/Middle Rest Elements in Tuple Types
在 TypeScript 中,元组类型旨在模拟具有特定长度和元素类型的数组。
¥In TypeScript, tuple types are meant to model arrays with specific lengths and element types.
ts
// A tuple that stores a pair of numberslet a: [number, number] = [1, 2];// A tuple that stores a string, a number, and a booleanlet b: [string, number, boolean] = ["hello", 42, true];
随着时间的推移,TypeScript 的元组类型变得越来越复杂,因为它们也用于在 JavaScript 中建模诸如参数列表之类的内容。因此,它们可以包含可选元素和剩余元素,甚至可以包含用于工具和可读性的标签。
¥Over time, TypeScript’s tuple types have become more and more sophisticated, since they’re also used to model things like parameter lists in JavaScript. As a result, they can have optional elements and rest elements, and can even have labels for tooling and readability.
tsTry
// A tuple that has either one or two strings.letc : [string, string?] = ["hello"];c = ["hello", "world"];// A labeled tuple that has either one or two strings.letd : [first : string,second ?: string] = ["hello"];d = ["hello", "world"];// A tuple with a *rest element* - holds at least 2 strings at the front,// and any number of booleans at the back.lete : [string, string, ...boolean[]];e = ["hello", "world"];e = ["hello", "world", false];e = ["hello", "world", true, false, true];
在 TypeScript 4.2 中,剩余元素的使用方式得到了特别扩展。在之前的版本中,TypeScript 仅允许在元组类型的最后一个位置使用 ...rest
元素。
¥In TypeScript 4.2, rest elements specifically been expanded in how they can be used.
In prior versions, TypeScript only allowed ...rest
elements at the very last position of a tuple type.
但是,现在剩余元素可以在元组中的任何位置出现。 - 只有一些限制。
¥However, now rest elements can occur anywhere within a tuple - with only a few restrictions.
tsTry
letfoo : [...string[], number];foo = [123];foo = ["hello", 123];foo = ["hello!", "hello!", "hello!", 123];letbar : [boolean, ...string[], boolean];bar = [true, false];bar = [true, "some text", false];bar = [true, "some", "separated", "text", false];
唯一的限制是,剩余元素可以放在元组中的任何位置,只要它后面没有其他可选元素或剩余元素。换句话说,每个元组只有一个剩余元素,并且剩余元素之后没有可选元素。
¥The only restriction is that a rest element can be placed anywhere in a tuple, so long as it’s not followed by another optional element or rest element. In other words, only one rest element per tuple, and no optional elements after rest elements.
tsTry
interfaceClown {/*...*/}interfaceJoker {/*...*/}letA rest element cannot follow another rest element.1265A rest element cannot follow another rest element.StealersWheel : [...Clown [], "me", ...Joker []];letAn optional element cannot follow a rest element.1266An optional element cannot follow a rest element.StringsAndMaybeBoolean : [...string[], boolean?];
这些非尾随 rest 元素可用于对接受任意数量的前导参数和后跟几个固定参数的函数进行建模。
¥These non-trailing rest elements can be used to model functions that take any number of leading arguments, followed by a few fixed ones.
tsTry
declare functiondoStuff (...args : [...names : string[],shouldCapitalize : boolean]): void;doStuff (/*shouldCapitalize:*/ false)doStuff ("fee", "fi", "fo", "fum", /*shouldCapitalize:*/ true);
即使 JavaScript 没有任何语法来建模前导剩余参数,我们仍然可以通过将 ...args
剩余参数声明为使用前导剩余元素的元组类型,将 doStuff
声明为接受前导参数的函数。这可以帮助对许多现有的 JavaScript 进行建模!
¥Even though JavaScript doesn’t have any syntax to model leading rest parameters, we were still able to declare doStuff
as a function that takes leading arguments by declaring the ...args
rest parameter with a tuple type that uses a leading rest element.
This can help model lots of existing JavaScript out there!
详情请见 查看原始拉取请求。
¥For more details, see the original pull request.
对 in
运算符的更严格检查
¥Stricter Checks For The in
Operator
在 JavaScript 中,在 in
运算符的右侧使用非对象类型会导致运行时错误。TypeScript 4.2 确保可以在设计时捕获此问题。
¥In JavaScript, it is a runtime error to use a non-object type on the right side of the in
operator.
TypeScript 4.2 ensures this can be caught at design-time.
tsTry
"foo" inType 'number' is not assignable to type 'object'.2322Type 'number' is not assignable to type 'object'.42 ;
此检查在大多数情况下都相当保守,因此如果你收到与此相关的错误,则很可能是代码中存在问题。
¥This check is fairly conservative for the most part, so if you have received an error about this, it is likely an issue in the code.
非常感谢我们的外部贡献者 Jonas Hübotter 贡献的 他们的拉取请求!
¥A big thanks to our external contributor Jonas Hübotter for their pull request!
--noPropertyAccessFromIndexSignature
当 TypeScript 首次引入索引签名时,你只能使用像 person["name"]
这样的 “bracketed” 元素访问语法来获取由它们声明的属性。
¥Back when TypeScript first introduced index signatures, you could only get properties declared by them with “bracketed” element access syntax like person["name"]
.
tsTry
interfaceSomeType {/** This is an index signature. */[propName : string]: any;}functiondoStuff (value :SomeType ) {letx =value ["someProperty"];}
当我们需要处理具有任意属性的对象时,这最终会变得繁琐。例如,假设一个 API,其中经常拼写错误属性名称,即在末尾添加一个额外的 s
字符。
¥This ended up being cumbersome in situations where we need to work with objects that have arbitrary properties.
For example, imagine an API where it’s common to misspell a property name by adding an extra s
character at the end.
tsTry
interfaceOptions {/** File patterns to be excluded. */exclude ?: string[];/*** It handles any extra properties that we haven't declared as type 'any'.*/[x : string]: any;}functionprocessOptions (opts :Options ) {// Notice we're *intentionally* accessing `excludes`, not `exclude`if (opts .excludes ) {console .error ("The option `excludes` is not valid. Did you mean `exclude`?");}}
为了简化此类情况,不久前,TypeScript 允许在类型具有字符串索引签名时使用类似 person.name
的 “dotted” 属性访问语法。这也使得将现有的 JavaScript 代码转换到 TypeScript 变得更加容易。
¥To make these types of situations easier, a while back, TypeScript made it possible to use “dotted” property access syntax like person.name
when a type had a string index signature.
This also made it easier to transition existing JavaScript code over to TypeScript.
但是,放宽限制也意味着拼写错误显式声明的属性变得更容易。
¥However, loosening the restriction also meant that misspelling an explicitly declared property became much easier.
tsTry
functionprocessOptions (opts :Options ) {// ...// Notice we're *accidentally* accessing `excludes` this time.// Oops! Totally valid.for (constexcludePattern ofopts .excludes ) {// ...}}
在某些情况下,用户更愿意明确选择使用索引签名。 - 当点式属性访问与特定属性声明不对应时,它们更倾向于收到错误消息。
¥In some cases, users would prefer to explicitly opt into the index signature - they would prefer to get an error message when a dotted property access doesn’t correspond to a specific property declaration.
这就是 TypeScript 引入新标志 noPropertyAccessFromIndexSignature
的原因。在此模式下,你将选择 TypeScript 的旧行为,即触发错误。此新设置不属于 strict
系列标志,因为我们相信用户会发现它在某些代码库上比其他代码库更有用。
¥That’s why TypeScript introduces a new flag called noPropertyAccessFromIndexSignature
.
Under this mode, you’ll be opted in to TypeScript’s older behavior that issues an error.
This new setting is not under the strict
family of flags, since we believe users will find it more useful on certain codebases than others.
你可以通过阅读相应的 拉取请求 来更详细地了解此功能。我们还要衷心感谢 Wenlu Wang 向我们发送此拉取请求!
¥You can understand this feature in more detail by reading up on the corresponding pull request. We’d also like to extend a big thanks to Wenlu Wang who sent us this pull request!
abstract
构造签名
¥abstract
Construct Signatures
TypeScript 允许我们将类标记为抽象类。这告诉 TypeScript,该类仅用于扩展,并且某些成员需要由任何子类填充才能实际创建实例。
¥TypeScript allows us to mark a class as abstract. This tells TypeScript that the class is only meant to be extended from, and that certain members need to be filled in by any subclass to actually create an instance.
tsTry
abstract classShape {abstractgetArea (): number;}newCannot create an instance of an abstract class.2511Cannot create an instance of an abstract class.Shape ();classSquare extendsShape {#sideLength: number;constructor(sideLength : number) {super();this.#sideLength =sideLength ;}getArea () {return this.#sideLength ** 2;}}// Works fine.newSquare (42);
为了确保在 new
中始终应用此限制,你不能将 abstract
类分配给任何需要构造签名的对象。
¥To make sure this restriction in new
-ing up abstract
classes is consistently applied, you can’t assign an abstract
class to anything that expects a construct signature.
tsTry
interfaceHasArea {getArea (): number;}letType 'typeof Shape' is not assignable to type 'new () => HasArea'. Cannot assign an abstract constructor type to a non-abstract constructor type.2322Type 'typeof Shape' is not assignable to type 'new () => HasArea'. Cannot assign an abstract constructor type to a non-abstract constructor type.: new () => Ctor HasArea =Shape ;
如果我们打算运行类似 new Ctor
的代码,这样做没有问题,但如果我们想编写 Ctor
的子类,这样做就过于严格了。
¥This does the right thing in case we intend to run code like new Ctor
, but it’s overly-restrictive in case we want to write a subclass of Ctor
.
tsTry
abstract classShape {abstractgetArea (): number;}interfaceHasArea {getArea (): number;}functionmakeSubclassWithArea (Ctor : new () =>HasArea ) {return class extendsCtor {getArea () {return 42}};}letArgument of type 'typeof Shape' is not assignable to parameter of type 'new () => HasArea'. Cannot assign an abstract constructor type to a non-abstract constructor type.2345Argument of type 'typeof Shape' is not assignable to parameter of type 'new () => HasArea'. Cannot assign an abstract constructor type to a non-abstract constructor type.MyShape =makeSubclassWithArea (); Shape
它也不能很好地与内置辅助类型(如 InstanceType
)配合使用。
¥It also doesn’t work well with built-in helper types like InstanceType
.
tsTry
typeMyInstance =InstanceType <typeofShape >;
这就是为什么 TypeScript 4.2 允许你在构造函数签名上指定 abstract
修饰符。
¥That’s why TypeScript 4.2 allows you to specify an abstract
modifier on constructor signatures.
tsTry
interfaceHasArea {getArea (): number;}// Works!letCtor : abstract new () =>HasArea =Shape ;
在构造函数签名中添加 abstract
修饰符,表示你可以传入 abstract
构造函数。它不会阻止你传入其他 “concrete” 类型的类/构造函数。 - 它实际上只是表示没有直接运行构造函数的意图,因此传入任何类类型都是安全的。
¥Adding the abstract
modifier to a construct signature signals that you can pass in abstract
constructors.
It doesn’t stop you from passing in other classes/constructor functions that are “concrete” - it really just signals that there’s no intent to run the constructor directly, so it’s safe to pass in either class type.
此功能允许我们以支持抽象类的方式编写混合工厂。例如,在下面的代码片段中,我们可以将 mixin 函数 withStyles
与 abstract
类 SuperClass
一起使用。
¥This feature allows us to write mixin factories in a way that supports abstract classes.
For example, in the following code snippet, we’re able to use the mixin function withStyles
with the abstract
class SuperClass
.
tsTry
abstract classSuperClass {abstractsomeMethod (): void;badda () {}}typeAbstractConstructor <T > = abstract new (...args : any[]) =>T functionwithStyles <T extendsAbstractConstructor <object>>(Ctor :T ) {abstract classStyledClass extendsCtor {getStyles () {// ...}}returnStyledClass ;}classSubClass extendswithStyles (SuperClass ) {someMethod () {this.someMethod ()}}
请注意,withStyles
演示了一条特定规则,其中扩展了泛型值且受抽象构造函数(如 Ctor
)约束的类(如 StyledClass
)也必须声明为 abstract
。这是因为无法知道是否传入了具有更多抽象成员的类,因此也无法知道子类是否实现了所有抽象成员。
¥Note that withStyles
is demonstrating a specific rule, where a class (like StyledClass
) that extends a value that’s generic and bounded by an abstract constructor (like Ctor
) has to also be declared abstract
.
This is because there’s no way to know if a class with more abstract members was passed in, and so it’s impossible to know whether the subclass implements all the abstract members.
你可以阅读有关抽象构造签名 关于其拉取请求 的更多信息。
¥You can read up more on abstract construct signatures on its pull request.
使用 --explainFiles
理解项目结构
¥Understanding Your Project Structure With --explainFiles
TypeScript 用户经常会使用 “为什么 TypeScript 包含此文件?” 运算符。推断程序的文件是一个复杂的过程,因此有很多原因可以解释为什么使用了特定的 lib.d.ts
组合,为什么包含 node_modules
中的某些文件,以及为什么包含某些文件,尽管我们认为指定 exclude
会将它们排除在外。
¥A surprisingly common scenario for TypeScript users is to ask “why is TypeScript including this file?“.
Inferring the files of your program turns out to be a complicated process, and so there are lots of reasons why a specific combination of lib.d.ts
got used, why certain files in node_modules
are getting included, and why certain files are being included even though we thought specifying exclude
would keep them out.
这就是 TypeScript 现在提供 explainFiles
标志的原因。
¥That’s why TypeScript now provides an explainFiles
flag.
sh
tsc --explainFiles
使用此选项时,TypeScript 编译器将提供一些非常详细的输出,说明文件最终出现在程序中的原因。为了更轻松地阅读,你可以将输出转发到文件,或通过管道传输到可以轻松查看的程序。
¥When using this option, the TypeScript compiler will give some very verbose output about why a file ended up in your program. To read it more easily, you can forward the output to a file, or pipe it to a program that can easily view it.
sh
# Forward output to a text filetsc --explainFiles > explanation.txt# Pipe output to a utility program like `less`, or an editor like VS Codetsc --explainFiles | lesstsc --explainFiles | code -
通常情况下,输出会首先列出包含 lib.d.ts
文件的原因,然后是本地文件的原因,最后是 node_modules
文件的原因。
¥Typically, the output will start out by listing out reasons for including lib.d.ts
files, then for local files, and then node_modules
files.
TS_Compiler_Directory/4.2.2/lib/lib.es5.d.tsLibrary referenced via 'es5' from file 'TS_Compiler_Directory/4.2.2/lib/lib.es2015.d.ts'TS_Compiler_Directory/4.2.2/lib/lib.es2015.d.tsLibrary referenced via 'es2015' from file 'TS_Compiler_Directory/4.2.2/lib/lib.es2016.d.ts'TS_Compiler_Directory/4.2.2/lib/lib.es2016.d.tsLibrary referenced via 'es2016' from file 'TS_Compiler_Directory/4.2.2/lib/lib.es2017.d.ts'TS_Compiler_Directory/4.2.2/lib/lib.es2017.d.tsLibrary referenced via 'es2017' from file 'TS_Compiler_Directory/4.2.2/lib/lib.es2018.d.ts'TS_Compiler_Directory/4.2.2/lib/lib.es2018.d.tsLibrary referenced via 'es2018' from file 'TS_Compiler_Directory/4.2.2/lib/lib.es2019.d.ts'TS_Compiler_Directory/4.2.2/lib/lib.es2019.d.tsLibrary referenced via 'es2019' from file 'TS_Compiler_Directory/4.2.2/lib/lib.es2020.d.ts'TS_Compiler_Directory/4.2.2/lib/lib.es2020.d.tsLibrary referenced via 'es2020' from file 'TS_Compiler_Directory/4.2.2/lib/lib.esnext.d.ts'TS_Compiler_Directory/4.2.2/lib/lib.esnext.d.tsLibrary 'lib.esnext.d.ts' specified in compilerOptions... More Library References...foo.tsMatched by include pattern '**/*' in 'tsconfig.json'
目前,我们不保证输出格式。 - 它可能会随着时间的推移而改变。因此,如果你有任何建议,我们很乐意改进此格式!
¥Right now, we make no guarantees about the output format - it might change over time. On that note, we’re interested in improving this format if you have any suggestions!
更多信息请见 查看原始拉取请求
¥For more information, check out the original pull request!
改进了逻辑表达式中未调用函数的检查
¥Improved Uncalled Function Checks in Logical Expressions
得益于 Alex Tarasyuk 的进一步改进,TypeScript 的未调用函数检查现在适用于 &&
和 ||
表达式。
¥Thanks to further improvements from Alex Tarasyuk, TypeScript’s uncalled function checks now apply within &&
and ||
expressions.
在 strictNullChecks
下,以下代码现在将出错。
¥Under strictNullChecks
, the following code will now error.
ts
function shouldDisplayElement(element: Element) {// ...return true;}function getVisibleItems(elements: Element[]) {return elements.filter((e) => shouldDisplayElement && e.children.length);// ~~~~~~~~~~~~~~~~~~~~// This condition will always return true since the function is always defined.// Did you mean to call it instead.}
详情请见 查看此处的拉取请求。
¥For more details, check out the pull request here.
解构变量可以明确标记为未使用
¥Destructured Variables Can Be Explicitly Marked as Unused
得益于 Alex Tarasyuk 的另一个拉取请求,现在可以通过在解构变量前添加下划线(_
字符)来将其标记为未使用。
¥Thanks to another pull request from Alex Tarasyuk, you can now mark destructured variables as unused by prefixing them with an underscore (the _
character).
ts
let [_first, second] = getValues();
以前,如果 _first
之后从未使用过,TypeScript 会在 noUnusedLocals
下报错。现在,TypeScript 会识别出 _first
是故意用下划线命名的,因为本来就没打算使用它。
¥Previously, if _first
was never used later on, TypeScript would issue an error under noUnusedLocals
.
Now, TypeScript will recognize that _first
was intentionally named with an underscore because there was no intent to use it.
更多详情,请查看 完整变更。
¥For more details, take a look at the full change.
可选属性和字符串索引签名之间的宽松规则
¥Relaxed Rules Between Optional Properties and String Index Signatures
字符串索引签名是一种对类似字典的对象进行类型化的方法,你可以使用任意键进行访问:
¥String index signatures are a way of typing dictionary-like objects, where you want to allow access with arbitrary keys:
tsTry
constmovieWatchCount : { [key : string]: number } = {};functionwatchMovie (title : string) {movieWatchCount [title ] = (movieWatchCount [title ] ?? 0) + 1;}
当然,对于任何尚未收录在字典中的电影名称,movieWatchCount[title]
将是 undefined
(TypeScript 4.1 添加了选项 noUncheckedIndexedAccess
,以便在从此类索引签名读取时包含 undefined
)。尽管很明显肯定有一些字符串不存在于 movieWatchCount
中,但由于 undefined
的存在,早期版本的 TypeScript 将可选对象属性视为无法分配给其他兼容的索引签名。
¥Of course, for any movie title not yet in the dictionary, movieWatchCount[title]
will be undefined
(TypeScript 4.1 added the option noUncheckedIndexedAccess
to include undefined
when reading from an index signature like this).
Even though it’s clear that there must be some strings not present in movieWatchCount
, previous versions of TypeScript treated optional object properties as unassignable to otherwise compatible index signatures, due to the presence of undefined
.
tsTry
typeWesAndersonWatchCount = {"Fantastic Mr. Fox"?: number;"The Royal Tenenbaums"?: number;"Moonrise Kingdom"?: number;"The Grand Budapest Hotel"?: number;};declare constwesAndersonWatchCount :WesAndersonWatchCount ;constmovieWatchCount : { [key : string]: number } =wesAndersonWatchCount ;// ~~~~~~~~~~~~~~~ error!// Type 'WesAndersonWatchCount' is not assignable to type '{ [key: string]: number; }'.// Property '"Fantastic Mr. Fox"' is incompatible with index signature.// Type 'number | undefined' is not assignable to type 'number'.// Type 'undefined' is not assignable to type 'number'. (2322)
TypeScript 4.2 允许进行此赋值。但是,它不允许将类型中包含 undefined
的非可选属性赋值,也不允许将 undefined
写入特定键:
¥TypeScript 4.2 allows this assignment. However, it does not allow the assignment of non-optional properties with undefined
in their types, nor does it allow writing undefined
to a specific key:
tsTry
typeBatmanWatchCount = {"Batman Begins": number | undefined;"The Dark Knight": number | undefined;"The Dark Knight Rises": number | undefined;};declare constbatmanWatchCount :BatmanWatchCount ;// Still an error in TypeScript 4.2.constType 'BatmanWatchCount' is not assignable to type '{ [key: string]: number; }'. Property '"Batman Begins"' is incompatible with index signature. Type 'number | undefined' is not assignable to type 'number'. Type 'undefined' is not assignable to type 'number'.2322Type 'BatmanWatchCount' is not assignable to type '{ [key: string]: number; }'. Property '"Batman Begins"' is incompatible with index signature. Type 'number | undefined' is not assignable to type 'number'. Type 'undefined' is not assignable to type 'number'.: { [ movieWatchCount key : string]: number } =batmanWatchCount ;// Still an error in TypeScript 4.2.// Index signatures don't implicitly allow explicit `undefined`.Type 'undefined' is not assignable to type 'number'.2322Type 'undefined' is not assignable to type 'number'.movieWatchCount ["It's the Great Pumpkin, Charlie Brown"] =undefined ;
新规则也不适用于数字索引签名,因为它们被假定为类似数组且密集:
¥The new rule also does not apply to number index signatures, since they are assumed to be array-like and dense:
tsTry
declare letsortOfArrayish : { [key : number]: string };declare letnumberKeys : { 42?: string };Type '{ 42?: string | undefined; }' is not assignable to type '{ [key: number]: string; }'. Property '42' is incompatible with index signature. Type 'string | undefined' is not assignable to type 'string'. Type 'undefined' is not assignable to type 'string'.2322Type '{ 42?: string | undefined; }' is not assignable to type '{ [key: number]: string; }'. Property '42' is incompatible with index signature. Type 'string | undefined' is not assignable to type 'string'. Type 'undefined' is not assignable to type 'string'.= sortOfArrayish numberKeys ;
你可以更好地了解此更改 通过阅读原始 PR。
¥You can get a better sense of this change by reading up on the original PR.
声明缺失的辅助函数
¥Declare Missing Helper Function
感谢 Alexander Tarasyuk 中的 社区拉取请求,我们现在可以快速修复基于调用点声明新函数和方法的问题!
¥Thanks to a community pull request from Alexander Tarasyuk, we now have a quick fix for declaring new functions and methods based on the call-site!
打破变更
¥Breaking Changes
我们始终致力于在发布版本中尽量减少重大变更。TypeScript 4.2 包含一些重大更改,但我们相信它们在升级过程中应该可以处理。
¥We always strive to minimize breaking changes in a release. TypeScript 4.2 contains some breaking changes, but we believe they should be manageable in an upgrade.
lib.d.ts
更新
¥lib.d.ts
Updates
与每个 TypeScript 版本一样,lib.d.ts
的声明(尤其是为 Web 上下文生成的声明)都已更改。有各种变化,但 Intl
和 ResizeObserver
最终可能是最具破坏性的。
¥As with every TypeScript version, declarations for lib.d.ts
(especially the declarations generated for web contexts), have changed.
There are various changes, though Intl
and ResizeObserver
’s may end up being the most disruptive.
noImplicitAny
错误适用于松散的 yield
表达式
¥noImplicitAny
Errors Apply to Loose yield
Expressions
当捕获到 yield
表达式的值,但 TypeScript 无法立即确定你希望它接收的类型(即 yield
表达式不是上下文类型)时,TypeScript 现在会触发隐式 any
错误。
¥When the value of a yield
expression is captured, but TypeScript can’t immediately figure out what type you intend for it to receive (i.e. the yield
expression isn’t contextually typed), TypeScript will now issue an implicit any
error.
tsTry
function*g1 () {const'yield' expression implicitly results in an 'any' type because its containing generator lacks a return-type annotation.7057'yield' expression implicitly results in an 'any' type because its containing generator lacks a return-type annotation.value =yield 1;}function*g2 () {// No error.// The result of `yield 1` is unused.yield 1;}function*g3 () {// No error.// `yield 1` is contextually typed by 'string'.constvalue : string = yield 1;}function*g4 ():Generator <number, void, string> {// No error.// TypeScript can figure out the type of `yield 1`// from the explicit return type of `g4`.constvalue = yield 1;}
在 相应的变更 中查看更多详细信息。
¥See more details in the corresponding changes.
扩展的未调用函数检查
¥Expanded Uncalled Function Checks
如上所述,使用 strictNullChecks
时,未调用函数检查现在将在 &&
和 ||
表达式中一致运行。这可能是新的中断的来源,但通常表明现有代码中存在逻辑错误。
¥As described above, uncalled function checks will now operate consistently within &&
and ||
expressions when using strictNullChecks
.
This can be a source of new breaks, but is typically an indication of a logic error in existing code.
JavaScript 中的类型参数不会被解析为类型参数
¥Type Arguments in JavaScript Are Not Parsed as Type Arguments
JavaScript 中已经不允许使用类型参数,但在 TypeScript 4.2 中,解析器将以更符合规范的方式解析它们。因此,在 JavaScript 文件中编写以下代码时:
¥Type arguments were already not allowed in JavaScript, but in TypeScript 4.2, the parser will parse them in a more spec-compliant way. So when writing the following code in a JavaScript file:
ts
f<T>(100);
TypeScript 将按以下 JavaScript 代码进行解析:
¥TypeScript will parse it as the following JavaScript:
js
f < T > 100;
如果你正在利用 TypeScript 的 API 解析 JavaScript 文件中的类型构造,这可能会对你造成影响,这种情况可能在尝试解析 Flow 文件时发生。
¥This may impact you if you were leveraging TypeScript’s API to parse type constructs in JavaScript files, which may have occurred when trying to parse Flow files.
有关检查内容的更多详细信息,请参阅 拉取请求。
¥See the pull request for more details on what’s checked.
展开的元组大小限制
¥Tuple size limits for spreads
可以使用 TypeScript 中的任何类型的展开语法 (...
) 来创建元组类型。
¥Tuple types can be made by using any sort of spread syntax (...
) in TypeScript.
ts
// Tuple types with spread elementstype NumStr = [number, string];type NumStrNumStr = [...NumStr, ...NumStr];// Array spread expressionsconst numStr = [123, "hello"] as const;const numStrNumStr = [...numStr, ...numStr] as const;
有时,这些元组类型可能会意外变得非常大,这会导致类型检查花费很长时间。TypeScript 不会让类型检查过程挂起(这在编辑器场景中尤其糟糕),而是设置了限制器来避免执行所有这些工作。
¥Sometimes these tuple types can accidentally grow to be huge, and that can make type-checking take a long time. Instead of letting the type-checking process hang (which is especially bad in editor scenarios), TypeScript has a limiter in place to avoid doing all that work.
你可以访问 查看此拉取请求 了解更多详情。
¥You can see this pull request for more details.
.d.ts
扩展不能用于导入路径
¥.d.ts
Extensions Cannot Be Used In Import Paths
在 TypeScript 4.2 中,导入路径在扩展名中包含 .d.ts
会报错。
¥In TypeScript 4.2, it is now an error for your import paths to contain .d.ts
in the extension.
ts
// must be changed to something like// - "./foo"// - "./foo.js"import { Foo } from "./foo.d.ts";
相反,你的导入路径应该反映加载器在运行时执行的操作。以下任何导入方法都可以替代使用。
¥Instead, your import paths should reflect whatever your loader will do at runtime. Any of the following imports might be usable instead.
ts
import { Foo } from "./foo";import { Foo } from "./foo.js";import { Foo } from "./foo/index.js";
恢复模板字面量推断
¥Reverting Template Literal Inference
此更改从 TypeScript 4.2 Beta 版中删除了一项功能。如果你尚未升级到我们上一个稳定版本,则不会受到影响,但你可能仍然对更改感兴趣。
¥This change removed a feature from TypeScript 4.2 beta. If you haven’t yet upgraded past our last stable release, you won’t be affected, but you may still be interested in the change.
TypeScript 4.2 的测试版对模板字符串的推断进行了更改。在此更改中,模板字符串字面量要么被赋予模板字符串类型,要么简化为多个字符串字面量类型。这些类型在赋值给可变变量时会扩展为 string
。
¥The beta version of TypeScript 4.2 included a change in inference to template strings.
In this change, template string literals would either be given template string types or simplify to multiple string literal types.
These types would then widen to string
when assigning to mutable variables.
ts
declare const yourName: string;// 'bar' is constant.// It has type '`hello ${string}`'.const bar = `hello ${yourName}`;// 'baz' is mutable.// It has type 'string'.let baz = `hello ${yourName}`;
这与字符串字面量推断的工作原理类似。
¥This is similar to how string literal inference works.
ts
// 'bar' has type '"hello"'.const bar = "hello";// 'baz' has type 'string'.let baz = "hello";
因此,我们认为使模板字符串表达式具有模板字符串类型就是 “consistent”;但是,从我们所见所闻来看,这并不总是可取的。
¥For that reason, we believed that making template string expressions have template string types would be “consistent”; however, from what we’ve seen and heard, that isn’t always desirable.
作为回应,我们恢复了此功能(以及潜在的重大更改)。如果你确实希望模板字符串表达式具有类似字面量的类型,则可以在其末尾添加 as const
。
¥In response, we’ve reverted this feature (and potential breaking change).
If you do want a template string expression to be given a literal-like type, you can always add as const
to the end of it.
ts
declare const yourName: string;// 'bar' has type '`hello ${string}`'.const bar = `hello ${yourName}` as const;// ^^^^^^^^// 'baz' has type 'string'.const baz = `hello ${yourName}`;
TypeScript 的 lift
回调在 visitNode
中使用了不同的类型
¥TypeScript’s lift
Callback in visitNode
Uses a Different Type
TypeScript 有一个 visitNode
函数,它接受一个 lift
函数作为参数。lift
现在期望 readonly Node[]
而不是 NodeArray<Node>
。从技术上讲,这是一个 API 重大更改,你可以在 此处 上了解更多信息。
¥TypeScript has a visitNode
function that takes a lift
function.
lift
now expects a readonly Node[]
instead of a NodeArray<Node>
.
This is technically an API breaking change which you can read more on here.