Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What are the new advantages of TypeScript 4.2

2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly explains "what are the new advantages of TypeScript 4.2". The content of the explanation is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn "what are the new advantages of TypeScript 4.2"?

To get started with TypeScript 4.2, you can install it through NuGet [2] or run the following command using npm:

Npm install typescript

Let's preview the new features of TypeScript 4.2:

Smarter type aliases are maintained

Pre / middle extension elements in tuple types

Stricter in operator checking

-- noPropertyAccessFromIndexSignature

Abstract build signature

Learn about your project structure through-- explainFiles

Improve the check of non-executable functions in logical expressions

You can explicitly mark deconstructed variables as unused

Loose rules between optional properties and string index signatures

Declare missing helper functions

Destructive change

Smarter type aliases are maintained

TypeScript has a way to declare new types called type aliases. If you write a series of functions that can handle three types of data: string | number | boolean, you can define a type alias to avoid repetitive work:

Type BasicPrimitive = number | string | boolean

TypeScript used a series of rules to guess when type aliases should be used and when all types should be printed. For example, take a look at the following code snippet:

Export type BasicPrimitive = number | string | boolean; export function doStuff (value: BasicPrimitive) {let x = value; return x;}

If we hover over the variable x in a Visual Studio, VS Code editor, or TypeScript runtime environment [3], we will see a quick display panel showing that the type of x is BasicPrimitive. Similarly, if we define a declaration file (.d.ts file) for this method, TypeScript will show that the doStuff return value is of type BasicPrimitive.

But what happens if we return BasicPrimitive or undefined?

Export type BasicPrimitive = number | string | boolean; export function doStuff (value: BasicPrimitive) {if (Math.random ())

< 0.5) { return undefined; } return value; } 我们可以在TypeScript 运行环境[4]中观察发生了什么。尽管我们希望看到 TypeScript 展示的 doStuff 返回值类型是 BasicPrimitive | undefined,但是实际情况是,显示的返回值类型为 string | number | boolean | undefined!这是怎么回事? 这要归因于 TypeScript 内部对于类型的解析方式。当创建了一个联合类型包含一个或多个其他的联合类型时,TypeScript 会将这些类型归一化为一个新的扁平的联合类型 —— 此时,原本类型的信息就丢失了。类型检查器将会查找 string | number | boolean | undefined 每种组合是否具有类型别名,即使这样,仍可能会得到多个 string | number | boolean 类型别名的结果。 在 TypeScript 4.2 中,我们的内部逻辑将更加智能。我们会通过保留类型的原始定义以及后续对其的更新,从而持续追踪该类型的构造变化。我们同时也会追踪被键入其他类型别名实例的类型别名,并加以区分。 能够根据你在代码中使用的方式打印出这些类型,意味着对于 TypeScript 使用者来说,可以避免看到那些令人厌恶的巨型类型定义;并且这种方式可以帮助转化出更优质的 .d.ts 文件输出、错误信息以及在编辑器中对变量展示的快速信息和帮助等。 更详细的内容,可以查阅改进保留实例间联合和相交类型别名的第一个Pull Request[5],以及随后的保留间接别名的第二个Pull Request[6]。 元组类型中的前置/中置的扩展元素 在 TypeScript 中,元组类型最初用于对特定长度和特定元素类型进行建模。 // 一个存放了一对数字的元组 let a: [number, number] = [1, 2]; // 一个存放了一个字符串、一个数字以及一个布尔值的元组 let b: [string, number, boolean] = ["hello", 42, true]; 随着版本的更新,TypeScript 元组变得越来越复杂,因为它们还被用于对像 JavaScript 中的参数列表一样的事务进行建模。这样带来的结果就是,元组类型可以包含可选元素以及扩展元素,甚至为了提高可读性和易用性,元素还可以拥有标签。 // 一个包含一个或两个字符串的元组 let c: [string, string?] = ["hello"]; c = ["hello", "world"]; // 一个包含一个或两个字符串,并且打了标签的元组 let d: [first: string, second?: string] = ["hello"]; d = ["hello", "world"]; // 一个包含扩展元素的元组 —— 前置元素至少包含两个字符串 // 后置元素可以包含任意多个布尔值 let e: [string, string, ...boolean[]]; e = ["hello", "world"]; e = ["hello", "world", false]; e = ["hello", "world", true, false, true]; 在 TypeScript 4.2 中,扩展元素的使用特别得到了扩展。在较早的版本中,TypeScript 只允许扩展元素出现在元组的最后位置。 但是现在,扩展元素可以出现在元组的任意位置 —— 仅仅需要满足一些限制条件。 let foo: [...string[], number]; foo = [123]; foo = ["hello", 123]; foo = ["hello!", "hello!", "hello!", 123]; let bar: [boolean, ...string[], boolean]; bar = [true, false]; bar = [true, "some text", false]; bar = [true, "some", "separated", "text", false]; 想让扩展元素放在元组的任意位置,唯一的限制条件就是:不能有可选元素在其后面的位置并且不能有其他的扩展元素。换句话说,每个元组只允许一个扩展元素,并且该扩展元素后序位置不能跟随一个可选元素。 interface Clown { /*...*/ } interface Joker { /*...*/ } let StealersWheel: [...Clown[], "me", ...Joker[]]; // ~~~~~~~~~~ Error! // 扩展元素后续不能有其他的扩展元素 let StringsAndMaybeBoolean: [...string[], boolean?]; // ~~~~~~~~ Error! // 扩展元素后序不能是可选元素 这些灵活的扩展元素,可以用于对具有任意数量前置参数以及固定格式后置参数的函数进行建模。 declare function doStuff(...args: [...names: string[], shouldCapitalize: boolean]): void; doStuff(/*shouldCapitalize:*/ false) doStuff("fee", "fi", "fo", "fum", /*shouldCapitalize:*/ true); 尽管 JavaScript 没有任何语法可以支持前置的扩展参数,我们仍可以使用 ...args 这种元组类型来实现上述 doStuff 那样具有前置扩展参数的函数。用这种方式可以对许多现有的 JavaScript 进行建模。 更多详细信息,请参考这个 Pull Request[7]。 更严格的 in 运算符检查 在 JavaScript 中,在 in 运算符右侧使用非对象类型是一个运行时错误。而在 TypeScript 4.2 中,该错误可以在代码设计时即被捕获。 "foo" in 42 // ~~ // 错误!'in' 表达式右侧不能为基数据类型 这项检查在大多数情况下都是相当保守的,如果你遇到过相关的错误提示,很可能是代码出了问题。 非常感谢我们的外围贡献者 Jonas Hübotter[8] 提出的 Pull Request[9]。 --noPropertyAccessFromIndexSignature 在 TypeScript 首次引入索引签名时,你只能使用诸如 person["name"] 这种 "中括号" 元素访问语法来声明对象的属性。 interface SomeType { /** 这是一个索引签名 */ [propName: string]: any; } function doStuff(value: SomeType) { let x = value["someProperty"]; } 如果我们需要一个具有任意属性的对象,这种方式将会非常麻烦。举个例子,假设有一个 API,犯了一个常见的拼写错误——在某个属性名字后面多加了一个 s。 interface Options { /** 需要排除的文件格式 */ exclude?: string[]; /** * 处理未声明为 'any' 的所有其他属性 */ [x: string]: any; } function processOptions(opts: Options) { // 注意这里我们故意访问的是 `excludes` 而非 `exclude` if (opts.excludes) { console.error("The option `excludes` is not valid. Did you mean `exclude`?"); } } 为了使这些类型更易用,前不久,TypeScript 允许了使用 "." 方法访问具有字符串索引签名对象(例如:person.name)的属性的语法。这也使得从现有 JavaScript 代码过渡为 TypeScript 代码变得容易。 但是,放松限制同时也意味着拼写错误导致的显示声明属性的错误访问会更加容易。 function processOptions(opts: Options) { // ... // 注意,这次我们是 "无意间" 访问了 `excludes` // 糟糕!完全奏效。 for (const excludePattern of opts.excludes) { // ... } } 某些情况下,用户只希望在显示声明的索引签名中进行访问——他们希望在使用点方法访问对象属性时,如果该属性不具有显示声明,则应当返回错误信息。 这就是 TypeScript 引入新的标识 --noPropertyAccessFromIndexSignature 的目的。开启这么模式,你将选择使用 TypeScript 旧的验证行为,从而在上述过程中抛出错误。这个新的设置不受严格模式的限制,因为我们相信用户会发现它在某些特定代码中会很有用。 你可以通过阅读这个 Pull Request[10] 获取对这个功能更详细的了解。同时,我们还要非常感谢 Wenlu Wang[11] 向我们提交了这个 Pull Request。 抽象构建签名 TypeScript 允许我们标记一个类为抽象类。这将告诉 TypeScript 该类只能被其他类扩展,并且扩展类必须包含确切的属性才能实例化。 abstract class Shape { abstract getArea(): number; } // 错误! 不能实例化一个抽象类。 new Shape(); class Square extends Shape { #sideLength: number; constructor(sideLength: number) { this.#sideLengthsideLength = sideLength; } getArea() { return this.#sideLength ** 2; } } // 可以正确执行 new Square(42); 为了确保这条限制在新建抽象类的过程中始终有效,你不能将抽象类赋值给任何需要构造签名的对象。 interface HasArea { getArea(): number; } // 错误!不能将抽象构造类型赋值给非抽象构造类型 let Ctor: new () =>

HasArea = Shape

If we intended to execute code like new Ctor, it would be the right thing to do if we threw an exception. But if we want to write subclasses of Ctor, this restriction is too strict.

Functon makeSubclassWithArea (Ctor: new () = > HasArea) {return class extends Ctor {getArea () {/ /...} let MyShape = makeSubclassWithArea (Shape)

Similarly, it does not work with built-in helper classes, such as InstanceType.

/ / error! / / Type 'typeof Shape' is not satisfied with the constraint' new (... args: any) = > any'. / / you cannot assign an abstract construction type to a non-abstract construction type type MyInstance = InstanceType

This is why TypeScript 4.2 allows you to specify abstract indicators in the construction signature.

Interface HasArea {getArea (): number;} / / successful! Let Ctor: abstract new () = > HasArea = Shape; / / ^

Adding an abstract indicator to the construction signature means that you can pass it in the abstract constructor. This does not prevent you from passing another "representational" class or constructor to it, but the indicator actually means that the class does not run the constructor directly, so it is safe to pass any type of class.

This feature allows us to write mixin factory functions in a way that contains abstract classes. For example, in the following code, we can use the mixin function withStyles that contains the abstract class SuperClass.

Abstract class SuperClass {abstract someMethod (): void; badda () {} type AbstractConstructor = abstract new (... args: any []) = > T function withStyles (Ctor: t) {abstract class StyledClass extends Ctor {getstyles () {/ /...}} return StyledClass } class SubClass extends withStyles (SuperClass) {someMethod () {this.someMethod ()}}

Note that withStyle shows a specific rule that classes (such as StyledClass) that extend from abstract constructors such as Ctor must also be declared as abstract classes. This is because there is no way to know if a class with more abstract members is passed in, and there is no way to know whether subclasses implement all abstract members.

You can see more abstract construction signatures in this Pull Request [12].

Learn about your project structure through-- explainFiles

A surprising and common scenario for TypeScript users is to be asked, "Why does TypeScript include this file?" Inferring program files is a complex process, which is why a specific combination of lib.d.ts is necessary, why specific files in node_modules need to be included, and why some files we think should be excluded but the results are included.

That's why TypeScript now provides the-- explainFiles flag.

Tsc-explainFiles

When you enable this option, the TypeScript compiler will give you some very lengthy output explaining why a file appears in the program. To make it easier to read, you can save the output to a file or pipe it to a program that is easier to read.

# Save the output to a file tsc-- explainFiles > expanation.txt # send the output to a utility program such as `less` through a pipeline, or an editor such as VS Code tsc-- explainFiles | less tsc-- explainFiles | code-

Typically, the output first lists the reasons why the lib.d.ts is included, then the local file, and finally the node_modules file.

TS_Compiler_Directory/4.2.2/lib/lib.es5.d.ts Library 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.ts Library 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.ts Library 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.ts Library 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.ts Library 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.ts Library 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.ts Library referenced via 'es2020' from file' TS_Compiler_Directory / 4.2.2 TS_Compiler_Directory/4.2.2/lib/lib.esnext.d.ts Library lib.esnext.d.ts' specified in compilerOptions lib. Esnext.d.ts' LBX' More Library References... Foo.ts Matched by include pattern'* * / *'in 'tsconfig.json'

Currently, we have not guaranteed the format of the output-it may change in subsequent updates. It is worth mentioning that if you have any good suggestions for format improvement, we are all very interested.

For more information, refer to the original Pull Request [13].

Improve the check of non-executable functions in logical expressions

Thanks to further improvements in Alex Tarasyuk [14], TypeScript non-executable function checking now applies to & & and | | expressions.

Now in-- strictNullChecks mode, the following code reports an error:

Function shouldDisplayElement (element: Element) {/ /. Return true;} function getVisibleItems (elements: Element []) {return elements.filter (e = > shouldDisplayElement & & e.children.length) / / ~ / this condition always returns true because the function is already defined. / / do you want to execute it instead? }

For more information, see Pull Request [15].

You can explicitly mark deconstructed variables as unused

Thanks to another Pull Request of Alex Tarasyuk [16], you can now mark a variable as unused by underlining it before deconstructing it.

Let [_ first, second] = getValues ()

In previous versions, if _ first was not used in later code, TypeScript threw a noUnusedLocals error. Now, TypeScript will realize that _ first only declares that it is intentional not to call.

For details, please see this Pull Request [17].

Loose rules between optional properties and string index signatures

A string index signature is a way to type a dictionary-like object-when you want to allow the object to contain any key name.

Const movieWatchCount: {[key: string]: number} = {}; function watchMovie (title: string) {movieWatchCount [title] = (movieWatchCount [title]? 0) + 1;}

Of course, now that the code does not contain any movie titles, movieWatchCount [title] will return undefined (an option has been added in TypeScript 4.1-noUncheckedIndexedAccess automatically adds the undefined optional type to the string index signature). Even though it is obvious that movieWatchCount will contain some strings later, previous versions of TypeScript will still treat optional object properties as not being assigned to other compatible index signatures because of the existence of undefined.

Type WesAndersonWatchCount = {"Fantastic Mr. Fox"?: number; "The Royal Tenenbaums"?: number; "Moonrise Kingdom"?: number; "The Grand Budapest Hotel"?: number;}; declare const wesAndersonWatchCount: WesAndersonWatchCount; const movieWatchCount: {[key: string]: number} = wesAndersonWatchCount; / / ~ error! / / Type 'WesAndersonWatchCount' cannot be assigned to' {[key: string]: number;}'. The attribute 'Fantastic Mr. Fox' is not compatible with the index signature. / / Type 'number | undefined' cannot be assigned to' number'. / / Type 'undefined' cannot be assigned to type' number'. (2322)

TypeScript 4.2 allows this assignment. However, it does not allow non-optional attributes of type undefined to be assigned, nor does it allow undefined to be written to a specific key:

Type BatmanWatchCount = {"Batman Begins": number | undefined; "The Dark Knight": number | undefined; "The Dark Knight Rises": number | undefined;}; declare const batmanWatchCount: BatmanWatchCount; / / TypeScript 4.2. Still error / / `undefined` command is ignored when the attribute is marked as optional. Const movieWatchCount: {[key: string]: number} = batmanWatchCount; / / TypeScript 4.2. An error will still be reported / / the index signature is not allowed to be defined as `undefined`. MovieWatchCount ["It's the Great Pumpkin, Charlie Brown"] = undefined

The new rules also do not apply to digital index signatures because they are assumed to be intensive data structures similar to arrays.

You can better understand this rule by reading this Pull Request [18].

Declare missing helper functions

Thanks to the community-based Pull Request implemented by Alex Tarasyuk [19], we now have a quick fix when declaring new functions and methods.

Picture

Destructive change

We are always committed to minimizing disruptive changes in the release. TypeScript 4.2 contains some disruptive changes, but we think they are controllable during the upgrade process.

Lib.d.ts upgrade

As with each version of TypeScript upgrade, the declaration of lib.d.ts (especially the one generated for the Web context) changes. There are many changes to the upgrade, but in the end it is Intl and ResizeObserver that may be the most disruptive.

OnImplicitAny errors apply to loose yield expressions

When the value of the yield expression is captured, but TypeScript cannot immediately tell which type you intend to accept (for example, the yield expression is not entered by context type), TypeScript now issues an error with the type implicitly declared as any.

Function* G1 () {const value = yield 1; / / ~ / / error! / / the yield' expression implicitly returns' any' type / / because it contains a generator that lacks return type comments. } function* G2 () {/ / correct. The result of / / `yield 1` is not used. Yield 1;} function* g3 () {/ / correct. / / `yield 1` is defined as' string' 'according to the context. Const value: string = yield 1;} function* g3 (): Generator {/ / correct. / / through the explicit return of the result in 'g3', / / TypeScript can know the type of' yield 1 'const value = yield 1;}

For details, please see this Pull Request [21].

Extended unexecuted function check

As mentioned above, when the-- strictNullChecks mode is enabled, the unexecuted function check is performed consistently in the & & and | | expressions. This can be potentially destructive, but it usually indicates a logic error in existing code.

Type parameters in JavaScript will not be parsed for untyped parameters

Type parameters are no longer allowed in JavaScript, but in TypeScript 4.2, the parser will parse them in a more canonical manner. So, when you write the following code in JavaScript:

F (100)

TypeScript parses it into the following code:

(F)

< T) >

(100)

You may be bothered when you use TypeScript's API to parse type constructs in JavaScript files.

The in operator no longer allows the value on the right to be the base type "foo" in 42 / / ~ ~ / / error!' Base types are not allowed on the right side of in' expressions

Please refer to this Pull Request [22] for details.

The size of extension operators in tuples is limited

Tuple types in TypeScript can be generated by any type of extended operation syntax

/ / tuple types generated by extension elements type NumStr = [number, string]; type NumStrNumStr = [... NumStr,... NumStr]; / / array extension operation const numStr = [123, "hello"] as const; const numStrNumStr = [... numStr,... numStr] as const

Sometimes, these tuple types can become unexpectedly large, which can make type checking take a long time. To prevent type checking from hanging (which is particularly bad in editor scenarios), TypeScript uses a limiter to prevent this from happening.

You can check the details in this Pull Request [23].

Files with .d.ts extension cannot be changed by Import// to be of the following type: / /-". / foo" /-". / foo.js" import {Foo} from ". / foo.d.ts"

The import path should reflect what the loader will do at run time. The following imports are equivalent:

Import {Foo} from ". / foo"; import {Foo} from ". / foo.js"; import {Foo} from ". / foo/index.js"; recovery template literal inference

This change is the removal of a feature from TypeScript 4.2 Beta. If you haven't upgraded to our latest stable version, you don't have to pay attention to this. However, you may also be interested in a brief understanding.

The Beta version of TypeScript 4.2 includes a change to template string inference. In this change, the template string literal is either defined as a given template string type or simplified to multiple string literal types. These types are expanded to string types when assigned to variables.

Declare const yourName: string; / / 'bar' is constant. / / it has the type '`hello ${string} `'. Const bar = `hello ${yourName} `; / / 'baz' is the variable / / it has the type' string'. Let baz = `hello ${yourName} `

This is similar to the way string literal inference works.

/ / the type of bar' is' hello'. Const bar = "hello"; / / the type of baz' is' string'. Let baz = "hello"

Therefore, we think that template string expressions with string types should be "constant". However, from what we have seen, this is not always desirable.

In response, we restored this feature (and potentially destructive). If you do want to define a template string expression as a literal type, you can add as const to it.

Declare const yourName: string; / / 'bar' owning type' `hello ${string} `'. Const bar = `hello ${yourName} `as const; / / ^ / / 'baz' owning type' string'. Const baz = `hello ${yourName} `; TypeScript lift Callback uses different types in visitNode

TypeScript has a visitNode function with lift function. Now, lift needs a read-only Node [] instead of NodeArray.

Thank you for your reading, the above is the content of "what are the new advantages of TypeScript 4.2". After the study of this article, I believe you have a deeper understanding of the new advantages of TypeScript 4.2.The specific use of TypeScript 4.2 also needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report