TypeScript 常用的新玩法
大家好,我是若川。持續組織了6個月源碼共讀活動,感興趣的可以點此加我微信 ruochuan12?參與,每周大家一起學習200行左右的源碼,共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》?包含20余篇源碼文章。歷史面試系列
上周分享了 ts 入門以及常用的小技巧《TS 在項目中的 N 個實用小技巧 - 文字稿》,然后發現有好幾個關注了公眾號來提問的(結果我開了小冰的自動回復,有了以下奇怪的對話
當看到想要回復的時候,發現...(怪我沒有開通知,略微尷尬?~~?😷
那就只能發文來講一講了(希望對應的小伙伴能看到)。回顧下之前的題目,問怎么能讓他的返回結果是 string
const?user?=?{name:?'amy',?age:?18} 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',?age:?18} type?UserType?=??typeof?userfunction?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:?stringconstructor(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?=?{?duration:?50,?title:?''?};a.duration?||=?10;?//?demo1,?=>?50a.duration?=?a.duration?||?10;?//?demo2,?=>?50if?(!a.duration)?{?//?demo3,?=>?50a.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; }?*/再有,通過字符串變量, lodash 的 get 方法也可以更加精準的定義,還有 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、拉你進源碼共讀群
今日話題
略。分享、收藏、點贊、在看我的文章就是對我最大的支持~
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的TypeScript 常用的新玩法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 线性时间选择—寻找第k小的数(分治算法)
- 下一篇: [html] iframe父页面如何获