typescript引入json文件-TypeScript 3.9 正式发布!平均编译时间从 26 秒缩短至 10 秒

2023-09-29 0 10,074 百度已收录

作者 | 微软官方博客

译者| 核子可乐

规划| 小智

来源 | 前端顶部

今天,微软在官方博客上宣布:TypeScript 3.9版本即将发布。 详细信息请参见下文。

有些同学可能对TypeScript不太熟悉。 这是一种基于JavaScript开发的语言,具有类型声明和注释等新类型的句子。 只有 TypeScript 编译器才能使用这种句型对代码进行类型检查,然后输出才能适应各种不同的运行时,成为清晰易读的 JavaScript 代码。

由于 TypeScript 具有丰富的跨编辑器功能,因此 TypeScript 中的静态类型检查可以在代码运行甚至文件保存之前快速指示代码中的错误。 除了错误检测之外,TypeScript 还允许用户在熟悉的编辑器中为 TypeScript 和 JavaScript 代码提供补全、快速修补和构建功能。 事实上,如果你之前使用过 Visual Studio 或 Visual Studio Code,TypeScript 可能已经为你前几年的 JavaScript 代码编译经验做出了贡献。 欲了解更多详细信息,请访问我们的网站。

而如果你已经在项目中使用过 TypeScript,你可以直接通过以下 npm 命令或通过 NuGet 快速获取我们发布的这个新版本:

npm install typescript

当然,您还可以通过以下方法获得编辑器支持:

下载对应版本的Visual Studio 2019/2017;

安装Visual Studio Code Insiders版本;

或者通过链接使用新的 TypeScript 版本;

l_using-较新的-typescript-版本

将 PackageControl 与 Sublime Text 3 结合使用。

在这个新版本中,我们团队非常注重性能、细节处理和稳定性。 我们仍在努力提高编译速度和编辑体验,消除滞后和复杂的细节,并减少错误和系统崩溃。 当然,我们也收到了来自外部社区的许多有价值的功能和修复贡献。

Inference 和 Promise.all 均得到改进

TypeScript 的最新版本(3.7 及更高版本)更新了 Promise.all 和 Promise.race 等函数的声明。 不幸的是,更新引入了新问题,特别是在混合空值或未定义值时。

interface Lion {

    roar(): void

}

interface Seal {

    singKissFromARose(): void

}

async function visitZoo(lionExhibit: Promise, sealExhibit: Promise<Seal | undefined>) {

    let [lion, seal] = await Promise.all([lionExhibit, sealExhibit]);

    lion.roar(); // uh oh

// ~~~~

// Object is possibly 'undefined'.

}

这种情况很奇怪! 事实上,sealExhibit 中包含的 undefined 相当于将 undefined 错误引入到 lion 类型中。

感谢 Jack Bates 的贡献,这个问题现已在 TypeScript 3.9 中得到修复。 上述错误不再存在。 如果您在早期版本的 TypeScript 中仍然面临 Promise 困惑,我们建议您尽快升级到 3.9 版本!

关于等待类型的更改

如果您仍在关注我们的问题跟踪器和设计讨论,您可能已经注意到我们正在开发一种名为 waiting 的新型运算符。 该运算符旨在精确模拟 JavaScript 中的 Promise 扩展。

我们最初预计在 TypeScript 3.9 中发布,但在使用现有代码库运行早期 TypeScript 版本时,我们意识到此功能需要进一步完善才能发布。 因此,我们决定将该功能从主分支中分离出来,直到它准备好为用户服务。 我们将对此功能进行更多实验,因此 waiting 将在 3.9 版本中暂时弃用。

速度改进

TypeScript 3.9 将带来一系列新的速度提升机制。 在发现Material-ui、Styled-Components等组件会带来极差的编辑/编译速度后,我们团队仍然努力优化性能。 我们对此领域进行了深入的研究,并提交了多个 Pull Request 来优化涉及小联合、交点、条件类型和地图类型的性能问题。

在某些代码库上,相关拉取请求的编译时间平均减少了 5% 到 10%。 总体而言,我们将material-ui-styles项目的编译时间减少了大约25%。 此外,我们还收到了微软团队的反馈,他们表示 TypeScript 3.9 的平均编译时间从 26 秒缩短到了 10 秒左右。

我们还对编辑器方案中的文件重命名功能进行了一些调整。 根据 Visual Studio Code 团队提供的建议,我们发现在执行文件重命名时,仅需要 5 到 10 秒才能找出需要更新哪些导出语句。 TypeScript 3.9 调整了内部编译器和语言服务缓存文件的搜索方式,成功解决了这个问题。

虽然还有改进的空间,但我们希望目前的结果能够给广大用户带来更好的用户体验!

// @ts-expect-error 注释

想象一下,如果我们用 TypeScript 编写一个库,并导入一个名为 doSTuff 的函数作为公共 API 的一部分。 该函数的类型声明需要两个字符串,以便其他 TypeScript 用户通常可以得到类型检查错误。 但同时,它还需要执行运行时检测(可能仅在开发版本中)以向 JavaScript 用户提示错误消息。

function doStuff(abc: string, xyz: string) {

    assert(typeof abc === "string");

    assert(typeof xyz === "string");

    // do some stuff

}

因此,一旦出现操作错误,TypeScript 用户就会面临红色乱码加上错误信息。 另一方面,JavaScript 用户面临断言错误。 我们想通过单元测试来检查实际情况是否符合预期。

expect(() => {

    doStuff(123, 456);

}).toThrow();

不幸的是,我们的测试是用 TypeScript 编写的,而 TypeScript 只能提示错误信息!

doStuff(123, 456);

//          ~~~

// error: Type 'number' is not assignable to type 'string'.

为此,TypeScript 3.9 带来了一个新功能: // @ts-expect-error 注释。 当一行代码以 // @ts-expect-error 注释为前缀时,TypeScript 严格禁止报告错误。 如果没有发生错误,TypeScript 会报告 // @ts-expect-error 不需要。

在下面的简单示例代码中,一切正常:

// @ts-expect-error

console.log(47 * "octopus");

但是下面的代码:

// @ts-expect-error

console.log(1 + 1);

会导致错误:

Unused '@ts-expect-error' directive.

我们要感谢此功能的贡献者 Josh Goldberg。 有关更多详细信息,请参阅 ts-expect-error 拉取请求:

ts-忽略还是 ts-预期错误?

某种程度上typescript引入json文件, // @ts-expect-error 可以作为抑制性注解,其效果与 // @ts-ignore 类似。 但两者的区别在于,如果下一行代码没有错误, // @ts-ignore 将不起作用。

您可能打算将现有的 // @ts-ignore 注解更改为 // @ts-expect-error,并且很好奇哪种方法更适合后续代码编译。 虽然确切的选择取决于您和您的团队,但这里有一些一般想法。

如果满足以下条件,则选择 ts-expect-error:

如果满足以下条件,请选择 ts-ignore:

检测条件表达式中未调用的函数

在 TypeScript 3.7 中,我们引入了未调用函数检查,以提醒您忘记调用的函数。

function hasImportantPermissions(): boolean {

    // ...

}

// Oops!

if (hasImportantPermissions) {

// ~~~~~~~~~~~~~~~~~~~~~~~
// This condition will always return true since the function is always defined.
// Did you mean to call it instead?

    deleteAllTheImportantFiles();

}

但是,此错误仅适用于 if 语句。 感谢 Alexander Tarasyuk 的贡献,该功能现在支持三种条件(即 cond ? trueExpr : falseExpr 语法)。

declare function listFilesOfDirectory(dirPath: string): string[];
declare function isDirectory(): boolean;
function getAllFiles(startFileName: string) {

    const result: string[] = [];
    traverse(startFileName);
    return result;

    function traverse(currentPath: string) {

        return isDirectory ?

        // ~~~~~~~~~~~
        // This condition will always return true
        // since the function is always defined.
        // Did you mean to call it instead?

            listFilesOfDirectory(currentPath).forEach(traverse) :
            result.push(currentPath);

    }

}

Alexander进一步提交了快速修复计划,以改善未调用函数检测功能的体验!

编辑器改进

TypeScript编译器不仅改善了大多数主流编辑器中的TypeScript编辑体验,还改善了Visual Studio系列编辑器中的JavaScript开发体验。 新的 TypeScript/JavaScript 功能将根据您使用的特定编辑器而有所不同。 以下是一些常见的改进:

JavaScript 中的 CommonJS 自动完成

新版本的另一个主要改进是使用 CommonJS 模块手动导出 JavaScript 文件。

在旧版本中,TypeScript 强制用户无论使用哪个文件都以 ECMAScript 模式导出,例如:

import * as fs from "fs";

但在编译 JavaScript 文件时,许多用户不准备使用 ECMScript 风格的模块。 很多同学还在使用CommonJS风格的require(...) import,例如:

const fs = require("fs");

TypeScript 现在还可以手动检查您正在使用的导出类型,以确保您的文件样式简单且一致。

有关更详细的信息,请参阅相应的拉取请求:

代码操作保留换行符

TypeScript 构建和快速修复通常难以正确保留换行符。 我们先看下面的简单代码示例:

const maxValue = 100;

/*start*/

for (let i = 0; i <= maxValue; i++) {

    // First get the squared value.

    let square = i ** 2;

    // Now print the squared value.

    console.log(square);

}

/*end*/

如果我们在编辑器中从 /*start*/ 到 /*end*/ 的突出显示区域中提取一个新函数,则生成的代码将如下所示:

const maxValue = 100;

printSquares();

function printSquares() {

    for (let i = 0; i <= maxValue; i++) {

        // First get the squared value.

        let square = i ** 2;

        // Now print the squared value.

        console.log(square);

    }

}

这是错误的——for循环中每句话之间原本有一个空行,但是构造之后,空行消失了! 好消息是 TypeScript 进行了许多改进,以确保您创作的内容准确。

const maxValue = 100;

printSquares();

function printSquares() {

    for (let i = 0; i <= maxValue; i++) {

        // First get the squared value.

        let square = i ** 2;

        // Now print the squared value.

        console.log(square);

    }

}

有关更详细的信息,请参阅相应的拉取请求:

快速修补缺失的返回表达式

在某些情况下,您可能会忘记返回函数中最后一句的值。 当向箭头函数添加花括号时尤其如此。

// before

let f1 = () => 42

// oops - not the same!

let f2 = () => { 42 }

感谢社区成员 Wenlu Wang 的贡献,TypeScript 现在可以快速修复添加缺失的 return 语句、删除大括号或向箭头函数实体(如对象文字)添加括号。

支持“解决方案样式”tsconfig.json 文件

编辑器需要确定当前文件属于哪个配置文件以及当前“项目”中还包括哪些其他文件,以选择适当的选项。 默认情况下,由 TypeScript 语言服务器支持的编辑器会在每个父目录中查找 tsconfig.json 来执行此操作。

但问题是,一些简单的 tsconfig.json 直接引用了其他 tsconfig.json 文件。

// tsconfig.json

{

    "files": [],

    "references": [

        { "path": "./tsconfig.shared.json" },
        { "path": "./tsconfig.frontend.json" },
        { "path": "./tsconfig.backend.json" },

    ]

}

也就是说,这个文件的作用只是管理其他项目文件; 在个人环境中,我们将此类文件称为“解决方案”。 显然,服务器无法正确提取此类 tsconfig.*.json 文件,但我们的目标是让语言服务器意识到当前的 .ts 文件可能属于 tsconfig.json 根目录中提到的其他项目。

TypeScript 3.9 解决了此支持问题。 有关更多详细信息,请参阅相应的拉取请求。

主要变化

解析可选链和非空断言之间的区别

TypeScript 最近实现了对可选链接运算符的支持,但根据用户反馈,非空断言运算符 (!) 的可选链接 (?.) 的行为不直观。

具体来说,在以前的版本中,代码:

foo?.bar!.baz

被解释为等同于以下 JavaScript 代码:

(foo?.bar).baz

上面的代码中,括号防止了可选链的“短路”行为; 因此,如果 foo 未定义,访问 baz 将导致运行时错误。

发现这个问题的 Babel 团队以及向我们提交反馈的大多数其他用户都认为这种行为是一个设计错误。 我们完全同意你的观点! 根据公众和我们自己内部的意见,由于该操作的目的是从 bar 类型中删除 null 和 undefined,因此! 操作员应该简单地“消失”。

换句话说,大多数人觉得上面的原始代码片段应该解释为:

foo?.bar.baz

,当foo未定义时,计算结果未定义。

这是一个重大变化,但我们认为大部分代码都是在考虑新的解释场景的情况下编写的。 如果您希望继续使用旧的行为,您可以这样做! 在运算符两侧添加括号,如下所示:

(foo?.bar)!.baz

} 和 > 现在是无效的 JSX 文本字符

JSX 规范严格禁止在文本位置使用 } 和 > 字符,TypeScript 和 Babel 遵循相同的规则。要在新版本中插入此类字符,需要使用 HTML 转义码(例如

2>1

)或插入带有字符串文字的表达式(例如

2 {">"} 1

)。

幸运的是,感谢 Brad Zacher 提交的 pull request,直接使用这两个符号现在会弹出以下错误消息:

Unexpected token. Did you mean `{'>'}` or `>`?

Unexpected token. Did you mean `{'}'}` or `&rbrace;`?

例如:

let directions = <div>Navigate to: Menu Bar > Tools > Options</div>

// ~ ~

// Unexpected token. Did you mean `{'>'}` or `>`?

由于 Alexander Tarasyuk 的贡献,此错误消息还附带了方便的快速修补功能,这使得批处理错误修补变得非常容易。

更严格地检测交叉点和可选属性

一般来说,如果A或B中的任何一个可以参数化为C,则A和B这样的交集类型可以参数化为C; 但有时,可选属性可能会导致问题。 例如:

interface A {

    a: number; // notice this is 'number'

}

interface B {

    b: string;

}

interface C {

    a?: boolean; // notice this is 'boolean'
    b: string;

}

declare let x: A & B;
declare let y: C;

y = x;

在以前版本的 TypeScript 中,上述代码只能工作,因为 A 与 C 完全不兼容,而 B 与 C 兼容。

在 TypeScript 3.9 中,只要交集中的每个类型都是具体对象类型,类型系统就会同时考虑所有属性。 因此,TypeScript 会意识到 A&B 中的 A 属性与 C 不兼容:

Type 'A & B' is not assignable to type 'C'.

  Types of property 'a' are incompatible.

    Type 'number' is not assignable to type 'boolean | undefined'.

更详细的信息请参考对应的pull request:

通过属性确定减少交集

在某些情况下,我们的类型可能描述一个不存在的值,例如:

declare function smushObjects<T, U>(x: T, y: U): T & U;

interface Circle {

    kind: "circle";
    radius: number;

}

interface Square {

    kind: "square";
    sideLength: number;

}

declare let x: Circle;
declare let y: Square;
let z = smushObjects(x, y);

console.log(z.kind);

这段代码有点奇怪,因为无法实际创建 Circle 和 Square 的交集 - 它们有两个相互不兼容的类型字段。 在以前的 TypeScript 版本中,此代码可以正常运行,但该类型本身被解释为从不,因为“圆”和“方形”描述的值集不存在。

在 TypeScript 3.9 中,类型系统变得更加严格 - 它会意识到 Circle 和 Square 由于不同的 kind 属性而不能相交。 因此,与旧版本将 z.kind 类型折叠为 never 不同,新版本会将 z 类型本身(圆形和方形)折叠为 never。 这意味着上面的代码现在会提示以下错误:

Property 'kind' does not exist on type 'never'.

通过观察,我们发现大多数中断都是由类型声明的缺陷引起的。 有关更多详细信息,请参阅原始拉取请求:

Getter/Setter 不再是可枚举属性

在旧版本的 TypeScript 中,类中的 get 和 set 访问器作为枚举发出; 这与 ECMAScript 规范明显不一致。 规范要求将两者设置为不可枚举属性。 因此,针对 ES5 和 ES2015 的 TypeScript 代码在实际执行中可能会导致不同的行为。

感谢 GitHub 用户 pathurs 的贡献,TypeScript 3.9 在这方面与 ECMAScript 要求保持一致。

扩展any的类型参数不再作为any执行

在旧版本的 TypeScript 中,受 any 约束的类型参数被视为 any。

function foo<T extends any>(arg: T) {

    arg.spfjgerijghoied; // no error!

}

这是一个明显的遗漏,因此 TypeScript 3.9 采用了更保守的方法,并对那些有问题的操作发出错误。

function foo<T extends any>(arg: T) {

    arg.spfjgerijghoied;

    // ~~~~~~~~~~~~~~~
    // Property 'spfjgerijghoied' does not exist on type 'T'.

}

始终保持导出*

在以前的 TypeScript 版本中,如果 foo 没有导入任何值,则诸如 export *from "foo" 之类的声明将直接从 JavaScript 输出中删除。 但这种方法并不合理,因为它是类型导向的并且很难用 Babel 进行模拟。 TypeScript 3.9 将始终保留导出 * 声明。 在实践中,这种调整不会对代码造成太大的实际影响,但可能会使捆绑器更难以对代码进行tree-shake。

更多版本变更请查看原始pull request:

导出当前用户的 Getter 以进行 Activity 绑定

当我们以 ES5 及更高版本中的 CommonJS 等模块系统为目标时,TypeScript 使用 get 访问器来模拟主动绑定typescript引入json文件,以便可以在任何导入的模块中公开对单个模块内变量的修改。 此更改的目标是进一步提高 TypeScript 输出代码与 ECMAScript 模块的兼容性。

有关更多详细信息,请参阅此更改的拉取请求:

导出结果和初始参数的改进

与 ES5 及更高版本中的 CommonJS 等目标模块系统结合使用,TypeScript 现在还可以将导入的声明提升到文件底部。 这一变化意味着 TypeScript 导入结果将越来越与 ECMAScript 模块兼容。 代码示例如下:

export * from "mod";

export const nameFromMod = 0;

之前的输出是:

__exportStar(exports, require("mod"));

exports.nameFromMod = 0;

但是,由于导入结果现在使用 get-accessor,__exportStar 的存在会导致形参操作失败,因为访问器不能简单地被形参覆盖。 在TypeSCript 3.9中,需要使用以下命令:

exports.nameFromMod = void 0;

__exportStar(exports, require("mod"));

exports.nameFromMod = 0;

有关详细信息,请参阅原始拉取请求:

下一阶段目标

我们希望 TypeScript 3.9 能够进一步增强您的日常开发体验并提高开发速度。 关于后续版本,欢迎您关注我们的4.0迭代计划和功能开发路线图。

4.0迭代计划:

功能开发路线图:

进一步阅读

推荐阅读:


玩转VS Code

VS Code · 编程开发 · 业界资讯

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悟空资源网 typescript typescript引入json文件-TypeScript 3.9 正式发布!平均编译时间从 26 秒缩短至 10 秒 https://www.wkzy.net/game/197975.html

常见问题

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务