网易首页 > 网易号 > 正文 申请入驻

TypeScript 5.7:一大波新特性来袭!

0
分享至

最近TypeScript 5.7发布了 RC 版本,其中包括了一大波新特性和优化措施,下面我们一起来学习下。

在 TypeScript 中,对于未初始化的变量,长期以来编译器已经可以捕获到一些问题了,特别是在所有前置分支中变量尚未初始化的情况下。

例如,对于下面这段代码:

let result: number;
if (someCondition()) {
    result = doSomeWork();
} else {
    let temporaryWork = doSomeWork();
    temporaryWork *= 2;
     // 忘记给 result 赋值了 
}

console.log(result);  // error: Variable 'result' is used before being assigned.

在这段代码中,由于result在所有可能的路径中并没有被保证初始化,因此编译器会报错,这一点 TypeScript 已经支持了很长时间。

但是,在有些情况下,分析就不那么准确了。例如,当变量在一个单独的函数中被访问时,类型系统无法确定该函数何时会被调用,往往会乐观地认为变量已经被初始化。

以下是这样的例子:

function foo() {
    let result: number;
    if (someCondition()) {
        result = doSomeWork();
    } else {
        let temporaryWork = doSomeWork();
        temporaryWork *= 2;
         // 忘记给 result 赋值了 
    }

    printResult();

    function printResult() {
        console.log(result);  // 这里不会报错 
    }
}

在上述代码中,虽然printResult函数内部使用了result变量,但由于类型系统无法判断printResult何时会被调用,它会默认认为result已经被初始化,而不会报错。

但是,在 TypeScript 5.7 中,即使变量可能已经被初始化,类型系统也会在变量完全未初始化的情况下报告错误。来看以下例子:

function foo() {
    let result: number;
    
     // 执行操作,但忘记给 result 赋值 

    function printResult() {
        console.log(result);  // error: Variable 'result' is used before being assigned. 
    }
}

在这段代码中,由于result在任何执行路径中都没有被赋值,类型系统会明确报错,指出未被初始化的变量使用了,这样可以帮助开发者更早发现程序中的潜在问题。

相对路径重写

现在有许多工具和运行时支持直接运行 TypeScript 代码,这意味着它们不需要生成输出 JavaScript 文件的构建步骤。例如,ts-nodetsxDenoBun都支持直接运行.ts文件。最近,Node.js 也在探讨这种支持,例如通过--experimental-transform-types--experimental-strip-types选项。这样做非常方便,因为它让我们可以更快速地迭代,而无需担心重新运行构建任务。

但是,当使用这些模式时,有一些复杂性需要注意。为了最大限度地兼容所有这些工具,直接导入的 TypeScript 文件必须在运行时使用适当的 TypeScript 扩展名。例如,要导入一个名为foo.ts的文件,我们需要在 Node.js 的新实验性支持中这样写:

// main.ts 

import * as foo from "./foo.ts";  // <- 这里需要 foo.ts,而不是 foo.js

通常情况下,如果我们这样写,TypeScript 会报错,因为它期望我们导入的是输出文件。由于有些工具允许.ts导入,TypeScript 早已通过一个名为--allowImportingTsExtensions的选项支持这种导入方式。这种做法虽然可行,但如果我们需要真正将这些.ts文件生成.js文件呢?这对于那些需要分发.js文件的库作者来说是一个需求。但在此之前,TypeScript 一直避免重写任何路径。

为了支持这种情况,我们新增了一个编译选项--rewriteRelativeImportExtensions。当导入路径是相对路径(以./../开头),以 TypeScript 扩展名(.ts.tsx.mts.cts)结尾,并且是非声明文件时,编译器会将路径重写为相应的 JavaScript 扩展名(.js.jsx.mjs.cjs)。

// 在使用 --rewriteRelativeImportExtensions 选项时... 

 // 这些路径会被重写。 
import * as foo from "./foo.ts";
import * as bar from "../someFolder/bar.mts";

 // 这些路径不会以任何方式被重写。 
import * as a from "./foo";
import * as b from "some-package/file.ts";
import * as c from "@some-scope/some-package/file.ts";
import * as d from "#/file.ts";
import * as e from "./file.js";

以上的特性让我们可以编写 TypeScript 代码 "原地" 运行,并在准备好后将其编译为 JavaScript。

需要注意的是,TypeScript 通常避免重写路径。原因有很多,但最明显的一个原因是动态导入。如果开发者写了如下代码,处理导入接收的路径就不是那么简单的事了。实际上,不可能在任何依赖项中重写import的行为。

function getPath() {
    if (Math.random() < 0.17) {
        return "./foo.ts";
    } else {
        return "./foo.js";
    }
}

let myImport = await import(getPath());

还有一个问题是(如上所述)只有相对路径会被重写,并且它们是“天真地”被重写。这意味着任何依赖 TypeScript 的baseUrlpaths的路径将不会被重写:

// tsconfig.json 

{
    "compilerOptions": {
        "module": "nodenext",
         // ... 
        "paths": {
            "@/*": ["./src/*"]
        }
    }
}

// 不会被转换,不会生效。 
import * as utilities from "@/utilities.ts";

同样,任何可能通过package.jsonexportsimports字段解析的路径也不会被重写:

// package.json 
{
    "name": "my-package",
    "imports": {
        "#root/*": "./dist/*"
    }
}

// 不会被转换,不会生效。 
import * as utilities from "#root/utilities.ts";

因此,如果你一直在使用带有多个包相互引用的工作空间式布局,可能需要使用带有作用范围的条件导出,以使其生效:

// my-package/package.json 

{
    "name": "my-package",
    "exports": {
        ".": {
            "@my-package/development": "./src/index.ts",
            "import": "./lib/index.js"
        },
        "./*": {
            "@my-package/development": "./src/*.ts",
            "import": "./lib/*.js"
        }
    }
}

每次你想要导入.ts文件时,可以使用node --conditions=@my-package/development运行。

注意我们为条件使用的作用域@my-package/development。这是为了避免与可能也使用development条件的依赖项发生冲突而做出的权宜之计。如果每个包都在他们的包里导出development,那么解析过程可能会尝试解析.ts文件,这不一定能成功。

支持 --target es2024 和 --lib es2024

TypeScript 5.7 现在支持--target es2024,这意味着用户可以将目标运行时定为 ECMAScript 2024。这一特性主要是为了启用新的--lib es2024选项,该选项包含了许多新特性,例如SharedArrayBufferArrayBufferObject.groupByMap.groupByPromise.withResolvers等等。此外,Atomics.waitAsync也从--lib es2022移动到了--lib es2024

需要注意的是,由于 SharedArrayBuffer 和 ArrayBuffer 的变化,它们现在有些不同了。为了弥合这个差距并且保留底层的缓冲区类型,所有类型化数组(例如Uint8Array等)现在也变成了泛型。

interface Uint8Array
      
 extends ArrayBufferLike = ArrayBufferLike> {      // ... }

每个类型化数组现在包含一个名为TArrayBuffer的类型参数,虽然这个类型参数有一个默认的类型参数,这样我们仍然可以使用Int32Array而不必显式地写成Int32Array

上层配置文件搜索

当使用 TSServer(如 Visual Studio 或 VS Code)在编辑器中加载 TypeScript 文件时,编辑器会尝试找到 "拥有" 该文件的相关tsconfig.json文件。为此,它会从正在编辑的文件所在的目录开始向上遍历目录树,寻找任何名为tsconfig.json的文件。

以前,这个搜索会在找到第一个tsconfig.json文件时停止;但是,想象一下如下的项目结构:

project/
├── src/
│   ├── foo.ts
│   ├── foo-test.ts
│   ├── tsconfig.json
│   └── tsconfig.test.json
└── tsconfig.json

在这个例子中,src/tsconfig.json是项目的主要配置文件,而src/tsconfig.test.json是用于运行测试的配置文件。

// src/tsconfig.json 
{
    "compilerOptions": {
        "outDir": "../dist"
    },
    "exclude": ["**/*.test.ts"]
}

// src/tsconfig.test.json 
{
    "compilerOptions": {
        "outDir": "../dist/test"
    },
    "include": ["**/*.test.ts"],
    "references": [
        { "path": "./tsconfig.json" }
    ]
}

// tsconfig.json 
{
    "files": [],
    "references": [
        { "path": "./src/tsconfig.json" },
        { "path": "./src/tsconfig.test.json" }
    ]
}

问题在于,当编辑foo-test.ts时,编辑器会找到project/src/tsconfig.json作为 "所有" 配置文件,但这并不是我们想要的。如果搜索在这里停止,这可能不是理想的结果。以前,唯一的解决办法是将src/tsconfig.json重命名为src/tsconfig.src.json,这样所有文件都会找到顶层的tsconfig.json,它引用了所有可能的项目。

project/
├── src/
│   ├── foo.ts
│   ├── foo-test.ts
│   ├── tsconfig.src.json
│   └── tsconfig.test.json
└── tsconfig.json

为了不让开发者必须这样做,TypeScript 5.7 现在会继续向上遍历目录树,在编辑器场景中找到其他合适的tsconfig.json文件。这样可以为项目的组织和配置文件的结构提供更多的灵活性。

在编辑器中更快地检查复杂项目的归属

想象一下,我们有一个大型代码库,结构如下:

packages
├── graphics/
│   ├── tsconfig.json
│   └── src/
│       └── ...
├── sound/
│   ├── tsconfig.json
│   └── src/
│       └── ...
├── networking/
│   ├── tsconfig.json
│   └── src/
│       └── ...
├── input/
│   ├── tsconfig.json
│   └── src/
│       └── ...
└── app/
    ├── tsconfig.json
    ├── some-script.js
    └── src/
        └── ...

每个packages目录中的文件夹都是一个独立的 TypeScript 项目,app目录是主项目,依赖于所有其他项目。

// app/tsconfig.json 
{
    "compilerOptions": {
         // ... 
    },
    "include": ["src"],
    "references": [
        { "path": "../graphics/tsconfig.json" },
        { "path": "../sound/tsconfig.json" },
        { "path": "../networking/tsconfig.json" },
        { "path": "../input/tsconfig.json" }
    ]
}

注意我们在app目录中有一个文件some-script.js。当我们在编辑器中打开some-script.js时,TypeScript 语言服务(也处理 JavaScript 文件的编辑器体验!)需要确定该文件属于哪个项目,以便应用正确的设置。

在这种情况下,最近的tsconfig.json并未包含some-script.js,但 TypeScript 将继续询问:“app/tsconfig.json 引用的某个项目是否包含some-script.js?”为了做到这一点,以前 TypeScript 会逐一加载每个项目,并在找到包含some-script.js的项目时停止。即使some-script.js不包含在根文件集中,TypeScript 仍会解析项目中的所有文件,因为根文件集中的某些文件可能间接引用some-script.js

在长时间的实践中,我们发现这种行为会在大型代码库中导致极端且不可预测的行为。开发人员会打开偶然的脚本文件,但发现整个代码库都被加载了,等待时间异常漫长。

幸运的是,任何可以被另一个(非工作区)项目引用的项目必须启用一个名为composite的标志,该标志强制要求所有输入源文件必须在创建项目时已知。因此,当探测复合项目时,TypeScript 5.7 只会检查文件是否属于该项目的根文件集。这应能避免常见的最坏情况行为的发生。

--module nodenext 模式下的 JSON 模块导入验证

--module nodenext模式下从.json文件导入时,TypeScript 现在会强制执行某些规则,以防止运行时错误。

例如,导入 JSON 文件时,必须包含type: "json"的导入属性:

import myConfig from "./myConfig.json";
 //                   ~~~~~~~~~~~~~~~~~ 
 // ❌ error: Importing a JSON file into an ECMAScript module requires a 'type: "json"' import attribute when 'module' is set to 'NodeNext'. 

import myConfig from "./myConfig.json" with { type: "json" };
 //                                          ^^^^^^^^^^^^^^^^ 
 // ✅ 这样是正确的,因为我们提供了 `type: "json"`

除此之外,TypeScript 不会生成 "具名" 导出,JSON 导入的内容只能通过默认导出来访问。

// ✅ 这样是可以的: 
import myConfigA from "./myConfig.json" with { type: "json" };
let version = myConfigA.version;

 /////////// 

import * as myConfigB from "./myConfig.json" with { type: "json" };

 // ❌ 这样是不行的: 
let version = myConfig.version;

 // ✅ 这样是可以的: 
let version = myConfig.default.version;
支持 Node.js 中的 V8 编译缓存

Node.js 22 支持一个新 APImodule.enableCompileCache()。这个 API 允许运行时重用第一次运行工具后的解析和编译工作的一部分。

TypeScript 5.7 现在利用了这个 API,使其能够更快地开始做有用的工作。在我们自己的一些测试中,我们发现运行tsc --version的速度提升了约 2.5 倍。

Benchmark 1: node ./built/local/_tsc.js --version (*without* caching)
  Time (mean ± σ):     122.2 ms ±   1.5 ms    [User: 101.7 ms, System: 13.0 ms]
  Range (min … max):   119.3 ms … 132.3 ms    200 runs
 
Benchmark 2: node ./built/local/tsc.js --version  (*with* caching)
  Time (mean ± σ):      48.4 ms ±   1.0 ms    [User: 34.0 ms, System: 11.1 ms]
  Range (min … max):    45.7 ms …  52.8 ms    200 runs
 
Summary
  node ./built/local/tsc.js --version ran
    2.52 ± 0.06 times faster than node ./built/local/_tsc.js --version
TypedArrays 现在是ArrayBufferLike的泛型

在 ECMAScript 2024 中,SharedArrayBufferArrayBuffer的类型略有不同。为了弥合这一差距并保留底层的缓冲类型,所有类型化数组(如Uint8Array等)现在也是泛型。

interface Uint8Array
      
 extends ArrayBufferLike = ArrayBufferLike> {      // ... }

每个类型化数组现在都包含一个名为TArrayBuffer的类型参数,尽管该类型参数有一个默认类型参数,因此用户仍然可以直接使用Int32Array,而不必显式地写成Int32Array

从非字面量方法名创建类中的索引签名

TypeScript 现在对于使用非字面量计算属性名声明的方法在类中具有更一致的行为。例如:

declare const symbolMethodName: symbol;

export class A {
    [symbolMethodName]() { return 1 };
}

以前,TypeScript 会将这个类视为如下:

export class A {
}

换句话说,从类型系统的角度来看,[symbolMethodName]对类A没有任何影响。

TypeScript 5.7 现在更有意义地看待方法[symbolMethodName](),并生成一个索引签名。因此,上面的代码现在解释为如下代码:

export class A {
    [x: symbol]: () => number;
}

这为对象字面量中的属性和方法提供了一致的行为。

在返回nullundefined的函数上增加隐式any错误

当一个函数表达式被上下文类型化为返回泛型类型的签名时,如果没有启用strictNullChecks,TypeScript 现在会根据noImplicitAny适当地提供隐式any错误。

declare var p: Promise

 ; const p2 = p.catch(() => null); //                 ~~~~~~~~~~ // error TS7011: Function expression, which lacks return-type annotation, implicitly has an 'any' return type.

这意味着如果一个函数返回nullundefined,但未明确指定返回类型,则会触发错误。这样可以确保代码更加严格和健壮,减少因不明确的类型导致的潜在问题。

最后

参考:https://devblogs.microsoft.com/typescript/announcing-typescript-5-7-rc/

点赞在看是最大的支持⬇️❤️⬇️

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
美媒:美国对大疆等所有外国制造新型无人机禁令引发美国消费者愤怒,“疯狂囤货”

美媒:美国对大疆等所有外国制造新型无人机禁令引发美国消费者愤怒,“疯狂囤货”

环球网资讯
2025-12-23 20:22:16
1986年韩先楚病危,硬撑着给余秋里打了个电话:有个事我得求你,请务必给我的秘书安排个工作

1986年韩先楚病危,硬撑着给余秋里打了个电话:有个事我得求你,请务必给我的秘书安排个工作

文史明鉴
2025-12-19 16:51:13
快手变快播?深夜上万直播间同时"搞黄色"裸聊,年度最大网络事故

快手变快播?深夜上万直播间同时"搞黄色"裸聊,年度最大网络事故

派大星纪录片
2025-12-23 16:49:00
68岁相声演员娶小30岁洋妻,生一屋混血儿移居国外,如今过得怎样

68岁相声演员娶小30岁洋妻,生一屋混血儿移居国外,如今过得怎样

丰谭笔录
2025-12-24 00:09:46
阚清子让微博炸锅了!被传孩子没留住?路透看样子夫妇俩心情很差

阚清子让微博炸锅了!被传孩子没留住?路透看样子夫妇俩心情很差

好贤观史记
2025-12-23 23:44:58
医生多次强调老人立刻停止食用4种坚果,吃得越多,肺会越差

医生多次强调老人立刻停止食用4种坚果,吃得越多,肺会越差

健康科普365
2025-12-22 09:17:36
A股分红派息转增一览(12月24日):4股今日股权登记

A股分红派息转增一览(12月24日):4股今日股权登记

每日经济新闻
2025-12-24 07:42:06
争议!2025十佳运动员评选:樊振东,陈梦,德约入围,莎头落选

争议!2025十佳运动员评选:樊振东,陈梦,德约入围,莎头落选

大秦壁虎白话体育
2025-12-24 13:40:47
对华反击计划已敲定!高市早苗兴奋之际发现:中国正狂抛美债

对华反击计划已敲定!高市早苗兴奋之际发现:中国正狂抛美债

小鬼头体育
2025-12-24 15:39:37
美女白色露脐T恤配蓝色瑜伽裤,性感值拉满,简直是行走的 荷尔蒙

美女白色露脐T恤配蓝色瑜伽裤,性感值拉满,简直是行走的 荷尔蒙

小乔古装汉服
2025-09-24 07:20:03
中国奉陪到底!局势紧张之际,《东京宣言》发布,高市换新打法

中国奉陪到底!局势紧张之际,《东京宣言》发布,高市换新打法

绝对军评
2025-12-24 15:45:48
老公驻派中东3年,我深夜给5岁女儿掖被角,她说:爸爸每天半夜来

老公驻派中东3年,我深夜给5岁女儿掖被角,她说:爸爸每天半夜来

朝暮书屋
2025-12-17 18:26:03
一笔失败的引援?拜仁新11号表现远不及预计,身价下滑1000万

一笔失败的引援?拜仁新11号表现远不及预计,身价下滑1000万

里芃芃体育
2025-12-24 03:00:03
3人20+力克国王!CC23分7板14助5断,德罗赞空砍37分,威少27+6+4

3人20+力克国王!CC23分7板14助5断,德罗赞空砍37分,威少27+6+4

无术不学
2025-12-24 14:10:18
乌军确认撤出西维尔斯克,库皮扬斯克取得进展,克宫拒绝圣诞停火

乌军确认撤出西维尔斯克,库皮扬斯克取得进展,克宫拒绝圣诞停火

鹰眼Defence
2025-12-24 16:19:36
打脸来得太快!徐湖平“装病”不到24小时,却被扒出频繁现身活动

打脸来得太快!徐湖平“装病”不到24小时,却被扒出频繁现身活动

有范又有料
2025-12-24 10:04:39
0+0+0!冒充NBA球员!开拓者用人方式引发质疑

0+0+0!冒充NBA球员!开拓者用人方式引发质疑

篮球教学论坛
2025-12-24 16:24:12
秦岚的脚部照片在网上爆红,这背后是否有利益链的争议?

秦岚的脚部照片在网上爆红,这背后是否有利益链的争议?

动物奇奇怪怪
2025-12-22 05:25:09
最新公开!爱泼斯坦自杀前给性侵犯写信:我们的总统也喜欢......

最新公开!爱泼斯坦自杀前给性侵犯写信:我们的总统也喜欢......

扬子晚报
2025-12-24 10:51:06
快船大胜!小卡41+8+5,哈登29+4+6,赛后小卡走过去,抱住伊森

快船大胜!小卡41+8+5,哈登29+4+6,赛后小卡走过去,抱住伊森

担酒
2025-12-24 14:02:23
2025-12-24 17:35:00
开源中国 incentive-icons
开源中国
每天为开发者推送最新技术资讯
7515文章数 34488关注度
往期回顾 全部

科技要闻

马斯克没想到的"中国速度"!2026值得期待

头条要闻

辽宁省三名厅官被开除党籍 一人被指未经批准出入国境

头条要闻

辽宁省三名厅官被开除党籍 一人被指未经批准出入国境

体育要闻

26岁广西球王,在质疑声中成为本土得分王

娱乐要闻

曝阚清子女儿早产但没保住

财经要闻

假冒的“晴王”葡萄,还在卖

汽车要闻

“运动版库里南”一月份亮相   或命名极氪9S

态度原创

数码
时尚
家居
本地
房产

数码要闻

三星电子放缓DDR4停产进程,拟与客户签订“不可取消”长期协议以锁定高利润

介绍一个如果你用了,你就看不到这篇文章的产品

家居要闻

法式大平层 智能家居添彩

本地新闻

云游安徽|一川江水润安庆,一塔一戏一城史

房产要闻

硬核!央企海口一线江景顶流红盘,上演超预期交付!

无障碍浏览 进入关怀版