likes
comments
collection
share

TypeScript 5.3,带来这些小惊喜

作者站长头像
站长
· 阅读数 11

首发于公众号 前端从进阶到入院,欢迎关注。

TypeScript 5.3 或将带来这些新特性

TypeScript 5.2 就要发布了。但 TypeScript 团队已经在努力开发 TypeScript 5.3 了。

他们最近发布了TypeScript 5.3 迭代计划,这是他们用于规划下个 TypeScript 版本可能包含的特性的文档。

我们可以通过这个迭代计划提前看到 TypeScript 5.3 新特性,并不能保证这些特性一定会实现,但它给出了一个不错的前瞻。

这里是我对 TypeScript 5.3 中最有趣的可能实现的特性总结。

Import 属性

TypeScript 5.3 可能会实现Import 属性,一个最近达到 Stage 3 的 TC39 提案。

Import 属性允许你为导入指定选项。例如,你可以指定一个 JSON 导入的类型:

import json from './foo.json' with { type: 'json' };

也可以指定一个动态导入的类型:

import('foo.json', { with: { type: 'json' } });

你可以重新导出一个模块,带上验证后的类型:

export { val } from './foo.js' with { type: "javascript" };

或者用一个验证过的类型实例化一个 worker:

new Worker('foo.wasm', {
  type: 'module',
  with: { type: 'webassembly' },
});

提出这个改变的动机是为 JavaScript 提供一种验证导入的 MIME 类型的方式。主要原因是安全性:“防止服务器意外提供了一个不同的 MIME 类型,导致代码被意外执行”。

支持throw表达式

有一个 JavaScript 语法是throw表达式。例如,你可以这样写:

const id = searchParams.id || throw new Error("id是必需的");

你可能会觉得奇怪,为什么这个在现有的 JavaScript 中不可用,但确实还不行,在 TypeScript 中这会抛出一个错误:

TypeScript 5.3,带来这些小惊喜

在 TypeScript 5.3 中实现 throw 表达式的可能性不大。这个提案仍处于 Stage 2 阶段,距离实现 TypeScript 需要的 Stage 3 阶段还有一段距离。

但 TypeScript 迭代计划中特别提到要“支持”这个提案。这意味着正在积极推进它,因此它可能在未来的 JavaScript/TypeScript 版本中实现。

Isolated Declarations

几周前我很幸运地有机会去伦敦的 Bloomberg 办公室与Titian聊天,他是这位 PR 的作者。

在一个有许多 package 的 monorepo 中,你可能有相互依赖的 package。你可能会构建一个非常深的,像“家族树”一样的设置,其中 package A 依赖 package B,依赖 package C,依赖 package D。

在这种情况下,TypeScript 的检查会变得非常缓慢。必须先检查 package D,然后是 C,然后是 B,最后是 A。

造成这种情况的原因是 TypeScript 必须自己打印每个 package 的声明文件(.d.ts 文件),这也意味着要对它们进行类型检查。这是一个缓慢的过程。

加速这一过程的一种方法是让一个更快的工具(比如esbuildswc)为每个 package 生成声明文件。但目前这还不可行。TypeScript 对需要为代码添加多少注解相当宽松。第三方工具不够智能,无法基于推断生成声明文件。

此时,隔离声明(Isolated Declarations)出现了,一种新的,更严格的 TypeScript 模式。

这是你可以添加到tsconfig.json的一个选项:

{
  "compilerOptions": {
    "isolatedDeclarations": true
  }
}

启用后,它会强制你更严格地添加注解。确切的严格级别仍在调整,可能会随时间改变。但作为例子,可能会必须对导出函数添加返回类型注解,以免 TypeScript 不得不推断它们。

在你抱怨之前(我曾明确表示不喜欢返回类型注解),你只需要在共享 package 上启用isolatedDeclarations - 你不用在应用程序代码上启用它。对共享 package 的限制可能是可取的,因为通常你确实希望对共享 package 添加更多注解。

Titian 和团队最近的演示展示了对一个大型 monorepo 的显著加速。我希望这能在 TypeScript 5.3 中实现。

在泛型函数中缩小类型

我对使用泛型函数的一个建议是“不要害怕使用as”。现有的 TypeScript 在泛型函数内部缩小类型方面表现不佳。

看看下面的例子。

TypeScript 5.3,带来这些小惊喜

这里,我们试图根据一个键从一个对象中返回一个值。如果传入'foo',我们返回一个字符串。如果传入'bar',我们返回一个数字。

但 TypeScript 报错了,尽管这段代码看起来是没问题的。

原因是 TypeScript 没有缩小 Example[T]到正确的键。对Example[T]的任何缩小都会导致它的类型变为never - 因此导致上面的错误。

目前让它工作的唯一方法是将其类型为as never:

function exampleFunc<T extends keyof Example>(key: T): Example[T] {
  if (key === 'foo') {
    return 'abc' as never;
  } else {
    return 123 as never;
  }
}

这也太怪了。

TypeScript 5.3可能会在这里实现一些改变。这里有一个关于这个改变动机的长期打开的 issue

我对此非常激动,这里的推断不行让我难受很久了,也阻碍人们尝试泛型。如果 TypeScript 在这种情况下更智能一些,教授泛型会容易很多。

字符串的宽松自动补全

TypeScript 有一个著名的 hack,可以使用 string & {}来获得字符串上的'宽松自动补全'。例如:

type IconSize = 'small' | 'medium' | 'large' | (string & {});

这个注解看起来可能很奇怪 - 但其目的是允许你向IconSize赋值任何内容,同时仍获取另外三个值的自动补全。

const icons: IconSize[] = [
  'small',
  'medium',
  'large',
  'extra-large',
  'anything-goes',
];

TypeScript 5.3 可能会实现一个使这个 hack 不必要的新特性。你将能够使用string作为类型并获得相同的自动补全:

type IconSize = 'small' | 'medium' | 'large' | string;

这个太美滋滋了,特别是因为 WebStorm 用户已经享有这个功能多年了

@types/node中的fetch

2022 年 2 月 1 日,Node.js 团队合并了一个拉取请求,将 Fetch API 添加到了 Node.js 中。这意味着 Node.js 将会有一个fetch函数,就像浏览器一样。

问题是,这还没有被添加到@types/node中。这个小问题导致了一个相对激烈的DefinitelyTyped issue讨论。

所以,TypeScript 团队介入查看这个问题,nice。

参考:www.totaltypescript.com/typescript-…