TypeScript 5.6 beta 发布:更完善的空值与真值检查、Iterator Helpers、支持禁用类型检查

开发 前端
为了解决这个问题,TypeScript 引入了 --noUncheckedSideEffectImports 配置,在启用此配置时,TS 会检查所有的副作用导入是否有效。

TypeScript 已于 2024.7.27 发布 5.6 beta 版本,你可以在 5.6 Iteration Plan 查看所有被包含的 Issue 与 PR。如果想要抢先体验新特性,执行:

$ npm install typescript@beta

来安装 beta 版本的 TypeScript,或在 VS Code 中安装 JavaScript and TypeScript Nightly ,并选择为项目使用 VS Code 的 TypeScript 版本(cmd + shift + p, 输入 select typescript version),来更新内置的 TypeScript 支持。

图片图片

本篇是笔者的第 12 篇 TypeScript 更新日志,上一篇是 「TypeScript 5.5 beta 发布:类型守卫推导、控制流分析优化、独立类型声明等」,你可以在此账号的创作中找到(或在掘金/知乎/Twitter搜索林不渡),接下来笔者也将持续更新 TypeScript 的 DevBlog 相关,感谢你的阅读。

更完善的空值与真值检查

TS 在 4.8 版本与 4.9 版本分别引入了「引用类型字面量值全等比较」与「NaN 相等检查」的功能,用于检查出代码中的疏漏:

const obj = {};

// Error: 此语句始终将返回 false,因为 JavaScript 中使用引用地址比较对象,而非实际值
if (obj === {}) {
}

const func = () => {};
// Error: 此表达式将始终返回 true,你是否想要调用 func ?
if(func) { }

// 此表达式将始终返回 false,你是否指 Number.isNaN(value) ?
if(value === NaN) {}

而在 5.6 版本,TS 继续完善了对这一类「可疑代码」的检查,现在能够在发现表达式计算结果始终为 TRUE 时抛出错误,如正则表达式,函数表达式等:

if (/0x[0-9a-f]/) {
  // Error: 此表达式将始终返回 true
  // ...
}

if (x => 0) {
  // Error: 此表达式将始终返回 true
  // ...
}

同时在 5.6 版本也进一步完善了对空值合并(??)语法的检查,有时候我们可能会粗心写出如下的代码:

const value = inital < input ?? 100;

我们的本意是为 input 应用默认值,但由于少了括号的分割,导致先进行左侧的比较后再尝试应用默认值。但我们知道,不同于 || 语法会在操作符左侧是 '' 、 0 、false等“空值”时也应用默认值,?? 一定会确保左侧是 null / undefined 才进行默认值引用,所以这里实际上永远也不会应用默认值(虽然应用顺序也不对就是了)。

现在,TypeScript 会检查出这种情况并给出警告:

// Error: ?? 操作符的右侧无法到达,因为操作符左侧永远不会是 null/undefined
const value = inital < input ?? 100;

需要注意的是,直接使用 true / false 这样的值仍然是允许的,因为这通常是有意为之:

while (true) {
    doStuff();

    if (something()) {
        break;
    }

    doOtherStuff();
}

如果你熟悉 ESLint,应该会想到 no-constant-binary-expression 这条规则,它们的效果基本是一致的。

迭代器帮助方法 Iterator Helper

此特性是对 TC39 提案 proposal-iterator-helpers 的同步,其为 JavaScript 内置的迭代器对象(Iterator)增加了一组接口用于降低其使用成本,除 map、filter、some 这些与数组上方法功能类似的接口外,还包括一部分特有的方法:

  • iterator.take(limit: number),限定迭代器能够产生有效值的次数,超过有效次数的 next 方法调用会返回 { value: undefined, done: true },即视为迭代结束。
function* naturals() {
    let i = 0;
    while (true) {
      yield i;
      i += 1;
    }
  }
  
  const result = naturals()
    .take(3);
  result.next(); //  {value: 0, done: false};
  result.next(); //  {value: 1, done: false};
  result.next(); //  {value: 2, done: false};
  result.next(); //  {value: undefined, done: true};
  • iterator.drop(limit: number),跳过迭代器的前数个值。
function* naturals() {
    let i = 0;
    while (true) {
      yield i;
      i += 1;
    }
  }
  
  const result = naturals()
    .drop(3);
  result.next(); //  {value: 3, done: false};
  result.next(); //  {value: 4, done: false};
  result.next(); //  {value: 5, done: false};
  • iterator.flatMap(mapper),类似于 RxJs 中的 flatMap 操作符,mapper 方法会再次返回一个 Iterator ,可以用来将多个 Iterator 合成一个,类似于 RxJs 中合并多个 Observable。
function* naturals() {
    let i = 0;
    while (true) {
      yield i;
      i += 1;
    }
  }
  
  const result = naturals()
    .drop(3);
  result.next(); //  {value: 3, done: false};
  result.next(); //  {value: 4, done: false};
  result.next(); //  {value: 5, done: false};
  • iterator.toArray(),用于将有限迭代器转换为数组。
function* naturals() {
    let i = 0;
    while (true) {
      yield i;
      i += 1;
    }
  }
  
  const result = naturals()
    .take(5)
    .toArray();
  
  result // [0, 1, 2, 3, 4]
  • Iterator.from(),用于从部署了 next 方法的对象结构生成一个标准迭代器,有点类似于 Array.from 方法。
class Iter {
    next() {
      return { done: false, value: 1 };
    }
  }
  
  const iter = new Iter();
  const wrapper = Iterator.from(iter);
  
  wrapper.next() // { value: 1, done: false }

这些方法明显受到了 RxJs 与 Ix 的影响,毕竟 Iterator 和 Observable 在许多方面是非常相似的。由于这些方法并不会在每个运行时中都支持,同时为了避免和已有的 Iterator 命名冲突,TypeScript 中引入了一个新的类型 BuiltinIterator 来部署这些接口。

支持任意模块标识符 Arbitrary Module Identifiers

TypeScript 现在允许使用任意的标识符名(Arbitrary Module Identifiers)称来定义模块的导出绑定:

// fruits.ts
const banana = "🍌";

export { banana as "🍌" };

// index.ts
import * as Fruits from './fruits';

Fruits['🍌'];

这一功能看起来很搞笑,但实际上,它在 ES2022 就已经得到支持,反而是 TypeScript 慢了一步。这一功能在 WASM 等场景下是有实用意义的:

import { "Foo::new" as Foo_new } from "./foo.wasm"

const foo = Foo_new()

export { Foo_new as "Foo::new" }

另外,这一功能是由 ESBuild 的作者实现的,参考 #58640。

使用 --noUncheckedSideEffectImports 检查副作用导入

JavaScript 中我们是可以直接导入一个文件而不指定导入值的,比如:

import '@inside/polyfills'
import './polyfills'

这种导入一般称为副作用导入,比如导入 Polyfills,导入 CSS/Less 文件等。但是在 TypeScript 中,副作用导入的行为会略显奇怪。如果这个导入路径是确实存在的,TypeScript 会加载并检查来自导入的类型,但是如果导入路径不存在,TypeScript 会直接忽略这条导入语句而不是抛出错误,所以大概率你要到 Bundler 层或者运行时才会发现这个问题。

为了解决这个问题,TypeScript 引入了 --noUncheckedSideEffectImports 配置,在启用此配置时,TS 会检查所有的副作用导入是否有效。

使用 --noCheck 跳过类型检查

TypeScript 5.6 引入了 --noCheck 配置来支持禁用所有的类型检查——需要注意的是,此配置并不意味着不会生成声明文件(你是否在找 --noEmit ),引入其的目的之一就是配合 --isolatedDeclarations 配置,在不进行类型检查的前提下快速生成声明文件。或者你也可以独立使用 tsc --noEmit 与 tsc --noCheck 来拆分构建阶段,前者负责类型检查,后者负责生成产物。

在这里稍微展开介绍一下几个相关配置:

  • noEmit,进行类型检查,不生成类型声明与编译产物
  • noCheck,不进行类型检查,生成类型声明与编译产物
  • declaration,生成类型声明,注意这个配置默认可是 false (若启用了 Project References,则是 true)
  • emitDeclarationOnly,仅生成类型声明,不生成编译产物
责任编辑:武晓燕 来源: 林不渡也不是不能渡
相关推荐

2024-09-10 09:03:54

微软TypeScript

2021-06-09 07:55:19

Typescript类型检查

2023-01-05 08:09:27

GroovyDSL​

2021-06-05 21:30:24

typescriptOverride检查

2013-07-09 14:41:58

C动态类型

2010-03-12 19:03:48

Python 拼写检查

2014-01-09 10:40:38

Cocos3.0 Be触控

2023-01-06 08:06:52

Groovy类型扩展

2021-03-09 16:03:02

微软Microsoft漏洞

2009-04-03 15:07:14

Debian HCL硬件驱动Linux

2022-05-30 10:18:41

Ubuntu物联网

2022-12-30 08:08:30

2020-08-19 08:29:22

JavaScript 对象函数

2009-08-18 09:32:21

Silverlight

2009-12-07 16:16:45

Windows 7磁盘检查

2024-05-11 10:19:31

TypeScript类型接口

2012-07-02 10:43:49

JVMGroovyJava

2012-04-19 09:50:53

Chrome 19Be新版发布

2024-04-12 12:36:06

JSJavaScrip方式

2009-11-24 17:52:46

PHP函数in_arr
点赞
收藏

51CTO技术栈公众号