0

TypeScript 新品尝鲜

 2 years ago
source link: https://segmentfault.com/a/1190000039977951
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

还记得我在《2020 年 JavaScript 状态调研报告小结》中提到的 2020 年全球开发者最喜欢的 JavaScript 写法是什么吗?-- 没错!TypeScript!。鉴于在项目中使用 TypeScript 确实有以下种种好处:

  1. 帮助开发者在「运行时」而非「编译时」及时感知类型/语法错误(⚠️ 注意,TypeScript 并非万能,它并不能帮助您捕捉请求错误或环境错误);
  2. 结合编辑器,提供智能提示,提升开发体验;
  3. 很少被提及的一点:更容易让服务端程序员理解代码,方便前后端交流;

本篇文章将向您介绍 TypeScript 自 3.7 版本以来更新的一些实用特性,希望您的代码能变得更加稳固,优雅。

1. 新支持特性

请注意,TypeScript 是 JavaScript 类型的超集,而非语法的超集,因此一些符合 JavaScript 语法规范的代码,在 TypeScript 中却可能报错,例如:

let x = 1
x = 'hello world'

因此,在现代工具链中,TypeScript 编译器甚至不被用作编译出指定版本 JavaScript 的工具(这通常是 babel 的工作),而是作为一种更强大的代码检查工具被使用。但是随着 TypeScript 版本的更新,一些新的 JavaScript 语法特性也逐渐被 TypeScript 支持,这使得开发者在一些场景下可以摆脱 babel 编译的过程,直接使用 TypeScript 编译生成最终的代码。

1.1 Optional Chaining & Nullish Coalescing

自 3.7 版本开始,TypeScript 支持了目前在 stage 4 阶段的 Optional ChainingNullish Coalescing语法。

1.2 Private Fields && Namespace exports

自 3.8 版本开始,Typescript 支持了目前在 stage 3 阶段的 Private Fields 语法。(通过底层使用 WeakMap 数据结构,该语法使得 JavaScript 类真正意义上拥有「私有属性」)

class Foo {
  #bar
}

同时,该版本还支持了 namespace exports 语法:

export * as utils from './utils'

1.3 Inference of class field types

自 4.0 版本开始,TypeScript 支持自动推导 class 中的属性类型,无需再显示声明。


2. 新类型:元组

TypeScript 4.0 开始支持两种新的元组类型声明方式:

  1. Variadic tuple types(可变元组类型)
  2. Labeled tuple types(命名元组类型)

2.1 可变元组类型

type Foo<T extends any[]> = [boolean, ...T, boolean]

通过这种声明方式,我们可以更精确的定义一个函数参数类型,在使用函数式编程时,这种定义方式就很有用。

2.2 命名元组类型

const Address = [string, number]

function setAddress(...args: Address) {
  // some code here
}

当我们这样定义函数入参后,在使用函数时,编辑器的智能提示只会提示我们参数类型,丢失了对参数含义的描述。为了改善这一点,我们可以通过 Labeled tuple types,我们可以这样定义参数:

const Address = [streetName: string, streetNumber: number]

function setAddress(...args: Address) {
  // some code here
}

这样,在调用函数时,我们的参数就获得了相应的语义,这使得代码更加容易维护。


3. 可递归调用类型

自 Typescript 3.7 开始,我们终于获得了只用一条类型声明声明 JSON 数据的能力:

type JSONValue = 
  | string
  | number
  | boolean
  | null
  | JSONValue[]
  | {
      [key: string]: JSONValue
    }

4. 错误和断言处理

4.1 // @ts-expect-error

TypeScript 3.9 给出了一个替代 // @ts-ignore 注释的方案:// @ts-expect-error

从字面上我们不难理解为什么后者是更优的选择:

  1. 显示声明了会报错的原因,而不只是一味的规避检查;
  2. 向前兼容,未来如果 TypeScript 支持某语法导致不再报错,TypeScript 会主动提示删除注释,这会让代码变得更加简洁;

4.2 unknown 类型

让我们想想这段代码会最终打印出什么:

try {
  willThrowAnError()
} catch (err) {
  console.log(typeof err.message)
}

答案是 "string" 吗,并非如此!因为 err.message 的值有可能是 undefined,甚至有可能在这里抛错,这取决于我们的函数 willThrowAnError 内部是如何定义的:

// err.message => undefined
function willThrowAnError() {
    throw 'hello world'
}

// err.message => throw an Error!
function willThrowAnError() {
  throw null
}

虽然第二种情况几乎不会发生,但这两个示例说明了 catch 参数类型的不确定性(因此在 TypeScript 中,它的默认类型是 any)。

因此,在 TypeScript 4.0 中,提供了 unknown 类型供我们处理这些我们「不知道」的类型。不同于 any 类型,unknown 是 TypeScript 中的第一类型,可以在任何地方使用。

子曰:知之为知之,不知为不知。

4.3 Assertion functions

自 TypeScript 3.7 开始,支持基于 return/throw 的类型断言。

function assertIsArray(val: any): asserts val is any[] {
  if (!Array.isArray(val)) throw new Error(`${val} is not an array`)
}

这将让测试变得更加容易。

点击此处浏览相关文档


5.1 Type-only imports

自 TypeScript 3.8 开始,TypeScript 支持仅引入模块类型:

import type { SomeThing } from "./some-module.js";

export type { SomeThing };

这样做的好处在于,当某个模块中包含副作用代码时,用户如果直接引入模块,就会无意间执行副作用代码,但当通过声明只引入类型时,则避免了这个隐患。

点击此处浏览相关文档

至此,本篇文章带您快速浏览了 TypeScript 3.7 以来一些实用性强的新特性,不知道您是否学有所获?TypeScript 将在 2021 年 5 月 25 日发布 4.3 版本,届时会新增哪些有意思的新特性?且听下回分解:)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK