TypeScript 常用的新玩法

2022年3月20日 341点热度 0人点赞 0条评论

大家好,我是若川持续组织了6个月源码共读活动,感兴趣的可以点此加我微信 ruochuan12 参与,每周大家一起学习200行左右的源码,共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试系列




上周分享了 ts 入门以及常用的小技巧《TS 在项目中的 N 个实用小技巧 - 文字稿,然后发现有好几个关注了公众号来提问的(结果我开了小冰的自动回复,有了以下奇怪的对话

图片

当看到想要回复的时候,发现...(怪我没有开通知,略微尴尬 ~~ ?

图片

那就只能发文来讲一讲了(希望对应的小伙伴能看到)。回顾下之前的题目,问怎么能让他的返回结果是 string

const user = {name'amy'age18}
function getPersonInfo(key: keyof typeof user{
  return user[key];
}

const userName = getPersonInfo('name'); // string | number

解决方法肯定是泛型。这里需要关注的一个点是,泛型是可以只定义传参不传的,例如下文的 T extends keyof UserType 就限定了 T 一定是 user 中的一个 key,这个时候将参数的类型定义定义成 T ,那么返回类型自然是 UserType[T] 了,这里返回类型不定义也成,ts 能根据下面的写法自动推断出来。这种通过泛型结合 extends 的写法,可以一定情况下来辅助指定参数的类型。

const user = {name'amy'age18}
type UserType =  typeof user

function getPersonInfo<T extends keyof UserType>(key: T): UserType[T{
  return user[key];
}

const userName = getPersonInfo('name'); // string

专门开了篇文章,那肯定得写点什么。上一篇文章做 ts 的分享的时候,发现市面上大多文章都是基于 ts3.x 的版本去做分析讲解的,就算高级进阶篇,很多也只是讲到一些常用的工具函数就戛然而至。甚至 google 搜索到的位于搜索引擎的 ts 中文网(据接触,有不少有中文文档就看中文文档的小伙伴),也只是更新到 3.1,从 npm 的发布记录来看也是四年前的版本了。所以,来写点大家说得相对少的,但是我们日常也可以感知到新玩意吧~~

正文开始

图片

几个对 ECMAScript 提案的跟进

一、可选操作链和空值合并

es2020 中,新增了一个深受大家喜爱的属性:proposal-optional-chaining ,通常来说我们要使用需要结合 babel 配合转译来兼容低版本的浏览器(使用 @babel/preset-env  的 ES2020 版本,或者使用 @babel/plugin-proposal-optional-chaining 插件。

另外还有一个运算符 ?? 空值合并(nullish coalescing operator)。例如 a ?? b 可以等同于 (a !== null && a !== undefined) ? a : b; 这个在做一些数据接口校验的时候有些作用。

typescript 3.7 版本中,针对上述说的两种操作符都及时做了支持,也就意味着如果你只使用了 typescript ,而没使用 babel  的场景也可以用它来玩(场景不多见,知道有这么回事就好)

let x = foo?.bar.baz();
// => let x = foo === null || foo === void 0 ? void 0 : foo.bar.baz();

let x = foo ?? bar();
// let x = foo !== null && foo !== void 0 ? foo : bar();

二、私有字段(Private Fields)

typescirpt 3.8 中支持使用 # 表示私有字段,另外在 4.3 版本中也支持了方法和 getter 都能有类似的写法。demo 如下:

class Person {
    #name: string
    constructor(name: string) {
        this.#name = name;
    }
    
    #someMethod() {
        //...
    }

    get #someValue() {
        return 100;
    }
}
let person = new Person('Amy');
person.#name
// Property '#name' is not accessible outside class 'Person'

其实跟 private 关键词差不多,不同的点在于上述代码,如果使用的是 private 属性,你通过 person['name'] 这种手段还是可以访问到,而你使用 person['#name'] 依然会报错

三、短路运算符

三个运算符新增 *= 的操作:&&= 、 ||=  和  ??=

跟常见的 let a += a+1 的感觉差不多,只不过这里的操作运算符是 && ||?? 而已,一个实际 demo,下面三种写法都是一个意思。

const a = { duration50title'' };

a.duration ||= 10// demo1, => 50

a.duration = a.duration || 10// demo2, => 50

if (!a.duration) { // demo3, => 50
    a.duration = 10
}

语言特性的优化

一、Type-Only Imports and Export

翻译过来,就是仅仅导入导出 type 。有一些用 ts 的小伙伴可能经常会看到一些warning 提示,找不到 xx 定义。但是点进文件一看,那些定义都好端端的写在文件中,于是一头雾水甚至直接忽略 warning 提示了。

图片

这其实是该功能会解决的一个问题,举一个例子来说明这情况产生的原因:

// types.ts
export type User = {... };
export type UserList = User[];

// index.ts
export { User, UserList } from './types'// ts types
export { getUser, CreateUser } from './user'// js function

从逻辑上看上面的代码并没有任何问题,但是在底层,这是一个被称之为「导入省略」的功能起的作用。通常 babel 在编译的时候,是一个个处理文件的,针对 ts 他一般是先删除类型,然后再进行编译。我们如果光看 index.ts ,实际都并不知道 User  和  CreateUser 谁是一个 ts type 的定义而谁是 js 运行时需要的东西。于是 babel 只能被迫的将所有东西都保留,于是转译后的文件为

// types.js
-- empty file --

// index.js
export { User, UserList } from './types'// ts types
export { getUser, CreateUser } from './user'// js function

而在 typescript 3.8 之后,我们的解决方法可以变成下述写法。针对 type 的导入或者导出,babel会在删除类型的环节,直接将 import type ... 或者 export type xxx 这类的语句直接去掉。

// index.ts
export type { User, UserList } from './types'// ts types
export { getUser, CreateUser } from './user'// js function

// => babel 转换后
export { getUser, CreateUser } from './user'// only js function

另外,在 4.5 的版本中,支持了对于某个变量局部使用 type 的写法,就不用说类型和 js 的函数要拆成两条语句了

// ts 3.8
import type { BaseType } from "./some-module.js";
import { someFunc } from "./some-module.js";

// => ts 4.5
import { someFunc, type BaseType } from "./some-module.js";

二、模版字符串

跟 es6 的模版字符串类似,不过是用于类型。此外,用在模板字符串类型中的泛型或类型别名,类型必须满足是string | number | bigint | boolean | null | undefined之一(也就是基础类型)。

应该有不少小伙伴都听说过,知乎上 ts 体操也是慢慢的从这特性出来开始越来越火。实际应用个人觉得会更多对于一些需要字符串拼接的场景,减少枚举。这里简单的列举一下例子

2.1 字符串组合场景

以下是一个 antd 中的 tootoolTip 组件,他有 12 个方向,传统写法,我们可能会直接枚举 12 种,写起来有那么一丢丢累,而且还很容易手抖不小心拼错。

图片

结合字符串模版和首字母大写的 Capitalize 方法,我们可以将纯枚举罗列,变成以下的组合:

图片

2.2 跟 infer 结合解析路由参数

网上看到的,话不多说,直接上代码。将 :id 转成 {id: string},不是特别理解的可以复习复习 infer 然后多看几眼自己尝试写一写。

图片

既然路由参数可以解析,那么 url 参数解析其实同理,想要将 a=1&b=2 转换成 { a:'1', b:'2' } 的话,自己撸的一个小思路:

图片

另外还有一个在 map 中使用 as rename 的方法,结合模版语法,我们可以在写一些通用函数的时候偷偷懒

type Getters<T> = {
    [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
};

interface Person {
    name: string;
    age: number;
}

type PersonGetters = Getters<Person>
/* => {
  getName: () => string;
  getAge: () => number;
} */

再有,通过字符串变量, lodashget 方法也可以更加精准的定义,还有 vuex 的模版等等。在 TSconf2020 中 anders 也给了很多不错的例子在 github 上,有兴趣的可以自行查阅:https://github.com/ahejlsberg/tsconf2020-demos/blob/master/template/main.ts。总之,就很多很多可以玩的玩法可以探索的,只要愿意。

图片

三、解构变量可以显式标记为未使用

使用解构的用法时,如果我们只需要第二个参数,而不需要第一个参数时,以前 ts 的语法检查总是会报错。在 typescript 4.2 版本之后,可以使用 _ 可以告诉 ts 这解构变量标记是未使用的,比如以下例子,只会报 second 没有被使用。

function getValues() {
    return ['a''b'];
}
const [_first, second] = getValues();
// 已声明“second”,但从未读取其值。

一个注意事项,如果你使用了 typescript-eslint 那可能编辑器的 eslint 检查还是会提示错误,需要配置让 no-unused-vars 规则允许下划线的变量不被使用。

rules: {
    "@typescript-eslint/no-unused-vars": [
   "error", { "ignoreRestSiblings"true }]
},

四、新增 Await 关键字

在 4.5 版本中支持,相当于可以快速获取 promise 的返回值了,结合 typeof 使用,或许可以节省几句对类型的 import

const a = Promise.resolve('100')
// A = string
type A = Awaited<typeof a>;
// B = number
type B = Awaited<Promise<Promise<number>>>;
// C = boolean | number
type C = Awaited<boolean | Promise<number>>;

最后

除了上述的一些描述,ts 每次更新当然也会有很多例如编译速度提升啊,更加符合 js 逻辑的一些自动推导的优化等等,这里就不做过多概述。还有一些配置项的新增,有兴趣的小伙伴可以自行查阅官方文档~~

相关回顾

1、TS 在项目中的 N 个实用小技巧 - 文字稿

生活总结

1、分享-前端小白的成长历程文字稿

2、2021 总结 | 鳗鱼 - 平凡的生活

3、2020 总结 | 鳗鱼 - 一起来吃鳗鱼饭吧

4、2019 总结 | 鳗鱼 - 写在 24 岁门口的自己

图片


················· 若川简介 ·················

你好,我是若川,毕业于江西高校。现在是一名前端开发“工程师”。写有《学习源码整体架构系列》20余篇,在知乎、掘金收获超百万阅读。
从2014年起,每年都会写一篇年度总结,已经坚持写了8年,点击查看年度总结
同时,最近组织了源码共读活动,帮助3000+前端人学会看源码。公众号愿景:帮助5年内前端人走向前列。

图片

扫码加我微信 ruochuan02、拉你进源码共读


今日话题

略。分享、收藏、点赞、在看我的文章就是对我最大的支持~
44400TypeScript 常用的新玩法

这个人很懒,什么都没留下

文章评论