自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

你不知道的 TypeScript 高級(jí)類型

開發(fā) 前端
如果只是掌握了 TypeScript 的一些基礎(chǔ)類型,可能很難游刃有余的去使用 TypeScript,而且最近 TypeScript 發(fā)布了 4.0 的版本新增了更多功能,想要用好它只能不斷的學(xué)習(xí)和掌握它。

[[342075]]

 前言

對(duì)于有 JavaScript 基礎(chǔ)的同學(xué)來說,入門 TypeScript 其實(shí)很容易,只需要簡單掌握其基礎(chǔ)的類型系統(tǒng)就可以逐步將 JS 應(yīng)用過渡到 TS 應(yīng)用。

  1. // js 
  2. const double = (num) => 2 * num 
  3.  
  4. // ts 
  5. const double = (num: number): number => 2 * num 

然而,當(dāng)應(yīng)用越來越復(fù)雜,我們很容易把一些變量設(shè)置為 any 類型,TypeScript 寫著寫著也就成了 AnyScript。為了讓大家能更加深入的了解 TypeScript 的類型系統(tǒng),本文將重點(diǎn)介紹其高級(jí)類型,幫助大家擺脫 AnyScript。

泛型

在講解高級(jí)類型之前,我們需要先簡單理解泛型是什么。

泛型是強(qiáng)類型語言中比較重要的一個(gè)概念,合理的使用泛型可以提升代碼的可復(fù)用性,讓系統(tǒng)更加靈活。下面是維基百科對(duì)泛型的描述:

泛型允許程序員在強(qiáng)類型程序設(shè)計(jì)語言中編寫代碼時(shí)使用一些以后才指定的類型,在實(shí)例化時(shí)作為參數(shù)指明這些類型。

泛型通過一對(duì)尖括號(hào)來表示(<>),尖括號(hào)內(nèi)的字符被稱為類型變量,這個(gè)變量用來表示類型。

  1. function copy<T>(arg: T): T { 
  2.   if (typeof arg === 'object') { 
  3.     return JSON.parse( 
  4.       JSON.stringify(arg) 
  5.     ) 
  6.   } else { 
  7.     return arg 
  8.   } 

這個(gè)類型 T,在沒有調(diào)用 copy 函數(shù)的時(shí)候并不確定,只有調(diào)用 copy 的時(shí)候,我們才知道 T 具體代表什么類型。

  1. const str = copy<string>('my name is typescript'

類型

我們?cè)?VS Code 中可以看到 copy 函數(shù)的參數(shù)以及返回值已經(jīng)有了類型,也就是說我們調(diào)用 copy 函數(shù)的時(shí)候,給類型變量 T 賦值了 string。其實(shí),我們?cè)谡{(diào)用 copy 的時(shí)候可以省略尖括號(hào),通過 TS 的類型推導(dǎo)是可以確定 T 為 string 的。

類型推導(dǎo)

高級(jí)類型

除了 string、number、boolean 這種基礎(chǔ)類型外,我們還應(yīng)該了解一些類型聲明中的一些高級(jí)用法。

交叉類型(&)

交叉類型說簡單點(diǎn)就是將多個(gè)類型合并成一個(gè)類型,個(gè)人感覺叫做「合并類型」更合理一點(diǎn),其語法規(guī)則和邏輯 “與” 的符號(hào)一致。

  1. T & U 

假如,我現(xiàn)在有兩個(gè)類,一個(gè)按鈕,一個(gè)超鏈接,現(xiàn)在我需要一個(gè)帶有超鏈接的按鈕,就可以使用交叉類型來實(shí)現(xiàn)。

  1. interface Button { 
  2.   type: string 
  3.   text: string 
  4.  
  5. interface Link { 
  6.   alt: string 
  7.   href: string 
  8.  
  9. const linkBtn: Button & Link = { 
  10.   type: 'danger'
  11.   text: '跳轉(zhuǎn)到百度'
  12.   alt: '跳轉(zhuǎn)到百度'
  13.   href: 'http://www.baidu.com' 

聯(lián)合類型(|)

聯(lián)合類型的語法規(guī)則和邏輯 “或” 的符號(hào)一致,表示其類型為連接的多個(gè)類型中的任意一個(gè)。

  1. T | U 

例如,之前的 Button 組件,我們的 type 屬性只能指定固定的幾種字符串。

  1. interface Button { 
  2.   type: 'default' | 'primary' | 'danger' 
  3.   text: string 
  4.  
  5. const btn: Button = { 
  6.   type: 'primary'
  7.   text: '按鈕' 

類型別名(type)

前面提到的交叉類型與聯(lián)合類型如果有多個(gè)地方需要使用,就需要通過類型別名的方式,給這兩種類型聲明一個(gè)別名。類型別名與聲明變量的語法類似,只需要把 const、let 換成 type 關(guān)鍵字即可。

  1. type Alias = T | U 
  2. type InnerType = 'default' | 'primary' | 'danger' 
  3.  
  4. interface Button { 
  5.   type: InnerType 
  6.   text: string 
  7.  
  8. interface Alert { 
  9.   type: ButtonType 
  10.   text: string 

類型索引(keyof)

keyof 類似于 Object.keys ,用于獲取一個(gè)接口中 Key 的聯(lián)合類型。

  1. interface Button { 
  2.     type: string 
  3.     text: string 
  4.  
  5. type ButtonKeys = keyof Button 
  6. // 等效于 
  7. type ButtonKeys = "type" | "text" 

還是拿之前的 Button 類來舉例,Button 的 type 類型來自于另一個(gè)類 ButtonTypes,按照之前的寫法,每次 ButtonTypes 更新都需要修改 Button 類,如果我們使用 keyof 就不會(huì)有這個(gè)煩惱。

  1. interface ButtonStyle { 
  2.     color: string 
  3.     background: string 
  4. interface ButtonTypes { 
  5.     default: ButtonStyle 
  6.     primary: ButtonStyle 
  7.     danger: ButtonStyle 
  8. interface Button { 
  9.     type: 'default' | 'primary' | 'danger' 
  10.     text: string 
  11.  
  12. // 使用 keyof 后,ButtonTypes修改后,type 類型會(huì)自動(dòng)修改  
  13. interface Button { 
  14.     type: keyof ButtonTypes 
  15.     text: string 

類型約束(extends)

這里的 extends 關(guān)鍵詞不同于在 class 后使用 extends 的繼承作用,泛型內(nèi)使用的主要作用是對(duì)泛型加以約束。我們用我們前面寫過的 copy 方法再舉個(gè)例子:

  1. type BaseType = string | number | boolean 
  2.  
  3. // 這里表示 copy 的參數(shù) 
  4. // 只能是字符串、數(shù)字、布爾這幾種基礎(chǔ)類型 
  5. function copy<T extends BaseType>(arg: T): T { 
  6.   return arg 

copy number

如果我們傳入一個(gè)對(duì)象就會(huì)有問題。

copy object

 

extends 經(jīng)常與 keyof 一起使用,例如我們有一個(gè)方法專門用來獲取對(duì)象的值,但是這個(gè)對(duì)象并不確定,我們就可以使用 extends 和 keyof 進(jìn)行約束。

  1. function getValue<T, K extends keyof T>(obj: T, key: K) { 
  2.   return obj[key
  3.  
  4. const obj = { a: 1 } 
  5. const a = getValue(obj, 'a'

獲取對(duì)象的值

這里的 getValue 方法就能根據(jù)傳入的參數(shù) obj 來約束 key 的值。

類型映射(in)

in 關(guān)鍵詞的作用主要是做類型的映射,遍歷已有接口的 key 或者是遍歷聯(lián)合類型。下面使用內(nèi)置的泛型接口 Readonly 來舉例。

  1. type Readonly<T> = { 
  2.     readonly [P in keyof T]: T[P]; 
  3. }; 
  4.  
  5. interface Obj { 
  6.   a: string 
  7.   b: string 
  8.  
  9. type ReadOnlyObj = Readonly<Obj> 

ReadOnlyObj

我們可以結(jié)構(gòu)下這個(gè)邏輯,首先 keyof Obj 得到一個(gè)聯(lián)合類型 'a' | 'b'。

  1. interface Obj { 
  2.     a: string 
  3.     b: string 
  4.  
  5. type ObjKeys = 'a' | 'b' 
  6.  
  7. type ReadOnlyObj = { 
  8.     readonly [P in ObjKeys]: Obj[P]; 

然后 P in ObjKeys 相當(dāng)于執(zhí)行了一次 forEach 的邏輯,遍歷 'a' | 'b'

  1. type ReadOnlyObj = { 
  2.     readonly a: Obj['a']; 
  3.     readonly b: Obj['b']; 

最后就可以得到一個(gè)新的接口。

  1. interface ReadOnlyObj { 
  2.     readonly a: string; 
  3.     readonly b: string; 

條件類型(U ? X : Y)

條件類型的語法規(guī)則和三元表達(dá)式一致,經(jīng)常用于一些類型不確定的情況。

  1. T extends U ? X : Y 

上面的意思就是,如果 T 是 U 的子集,就是類型 X,否則為類型 Y。下面使用內(nèi)置的泛型接口 Extract 來舉例。

  1. type Extract<T, U> = T extends U ? T : never; 

如果 T 中的類型在 U 存在,則返回,否則拋棄。假設(shè)我們兩個(gè)類,有三個(gè)公共的屬性,可以通過 Extract 提取這三個(gè)公共屬性。

  1. interface Worker { 
  2.   name: string 
  3.   age: number 
  4.   email: string 
  5.   salary: number 
  6.  
  7. interface Student { 
  8.   name: string 
  9.   age: number 
  10.   email: string 
  11.   grade: number 
  12.  
  13.  
  14. type CommonKeys = Extract<keyof Worker, keyof Student> 
  15. // 'name' | 'age' | 'email' 

CommonKeys

工具泛型

TypesScript 中內(nèi)置了很多工具泛型,前面介紹過 Readonly、Extract 這兩種,內(nèi)置的泛型在 TypeScript 內(nèi)置的 lib.es5.d.ts 中都有定義,所以不需要任何依賴都是可以直接使用的。下面看看一些經(jīng)常使用的工具泛型吧。

lib.es5.d.ts

Partial

  1. type Partial<T> = { 
  2.     [P in keyof T]?: T[P] 

Partial 用于將一個(gè)接口的所有屬性設(shè)置為可選狀態(tài),首先通過 keyof T,取出類型變量 T 的所有屬性,然后通過 in 進(jìn)行遍歷,最后在屬性后加上一個(gè) ?。

我們通過 TypeScript 寫 React 的組件的時(shí)候,如果組件的屬性都有默認(rèn)值的存在,我們就可以通過 Partial 將屬性值都變成可選值。

  1. import React from 'react' 
  2.  
  3. interface ButtonProps { 
  4.   type: 'button' | 'submit' | 'reset' 
  5.   text: string 
  6.   disabled: boolean 
  7.   onClick: () => void 
  8.  
  9. // 將按鈕組件的 props 的屬性都改為可選 
  10. const render = (props: Partial<ButtonProps> = {}) => { 
  11.   const baseProps = { 
  12.     disabled: false
  13.     type: 'button'
  14.     text: 'Hello World'
  15.     onClick: () => {}, 
  16.   } 
  17.   const options = { ...baseProps, ...props } 
  18.   return ( 
  19.     <button 
  20.       type={options.type} 
  21.       disabled={options.disabled} 
  22.       onClick={options.onClick}> 
  23.       {options.text} 
  24.     </button> 
  25.   ) 

Required

  1. type Required<T> = { 
  2.     [P in keyof T]-?: T[P] 

Required 的作用剛好與 Partial 相反,就是將接口中所有可選的屬性改為必須的,區(qū)別就是把 Partial 里面的 ? 替換成了 -?。

Record

  1. type Record<K extends keyof any, T> = { 
  2.     [P in K]: T 

Record 接受兩個(gè)類型變量,Record 生成的類型具有類型 K 中存在的屬性,值為類型 T。這里有一個(gè)比較疑惑的點(diǎn)就是給類型 K 加一個(gè)類型約束,extends keyof any,我們可以先看看 keyof any 是個(gè)什么東西。

keyof any

大致一直就是類型 K 被約束在 string | number | symbol 中,剛好就是對(duì)象的索引的類型,也就是類型 K 只能指定為這幾種類型。

我們?cè)跇I(yè)務(wù)代碼中經(jīng)常會(huì)構(gòu)造某個(gè)對(duì)象的數(shù)組,但是數(shù)組不方便索引,所以我們有時(shí)候會(huì)把對(duì)象的某個(gè)字段拿出來作為索引,然后構(gòu)造一個(gè)新的對(duì)象。假設(shè)有個(gè)商品列表的數(shù)組,要在商品列表中找到商品名為 「每日?qǐng)?jiān)果」的商品,我們一般通過遍歷數(shù)組的方式來查找,比較繁瑣,為了方便,我們就會(huì)把這個(gè)數(shù)組改寫成對(duì)象。

  1. interface Goods { 
  2.   id: string 
  3.  name: string 
  4.   price: string 
  5.   image: string 
  6.  
  7. const goodsMap: Record<string, Goods> = {} 
  8. const goodsList: Goods[] = await fetch('server.com/goods/list'
  9.  
  10. goodsList.forEach(goods => { 
  11.   goodsMap[goods.name] = goods 
  12. }) 

Pick

  1. type Pick<T, K extends keyof T> = { 
  2.     [P in K]: T[P] 

Pick 主要用于提取接口的某幾個(gè)屬性。做過 Todo 工具的同學(xué)都知道,Todo工具只有編輯的時(shí)候才會(huì)填寫描述信息,預(yù)覽的時(shí)候只有標(biāo)題和完成狀態(tài),所以我們可以通過 Pick 工具,提取 Todo 接口的兩個(gè)屬性,生成一個(gè)新的類型 TodoPreview。

  1. interface Todo { 
  2.   title: string 
  3.   completed: boolean 
  4.   description: string 
  5.  
  6. type TodoPreview = Pick<Todo, "title" | "completed"
  7.  
  8. const todo: TodoPreview = { 
  9.   title: 'Clean room'
  10.   completed: false 

TodoPreview

Exclude

  1. type Exclude<T, U> = T extends U ? never : T 

Exclude 的作用與之前介紹過的 Extract 剛好相反,如果 T 中的類型在 U 不存在,則返回,否則拋棄。現(xiàn)在我們那之前的兩個(gè)類舉例,看看 Exclude 的返回結(jié)果。

  1. interface Worker { 
  2.   name: string 
  3.   age: number 
  4.   email: string 
  5.   salary: number 
  6.  
  7. interface Student { 
  8.   name: string 
  9.   age: number 
  10.   email: string 
  11.   grade: number 
  12.  
  13.  
  14. type ExcludeKeys = Exclude<keyof Worker, keyof Student> 
  15. // 'name' | 'age' | 'email' 

ExcludeKeys

取出的是 Worker 在 Student 中不存在的 salary。

Omit

  1. type Omit<T, K extends keyof any> = Pick< 
  2.   T, Exclude<keyof T, K> 

Omit 的作用剛好和 Pick 相反,先通過 Exclude 先取出類型 T 中存在,但是 K 不存在的屬性,然后再由這些屬性構(gòu)造一個(gè)新的類型。還是通過前面的 Todo 案例來說,TodoPreview 類型只需要排除接口的 description 屬性即可,寫法上比之前 Pick 精簡了一些。

  1. interface Todo { 
  2.   title: string 
  3.   completed: boolean 
  4.   description: string 
  5.  
  6. type TodoPreview = Omit<Todo, "description"
  7.  
  8. const todo: TodoPreview = { 
  9.   title: 'Clean room'
  10.   completed: false 

TodoPreview

總結(jié)

如果只是掌握了 TypeScript 的一些基礎(chǔ)類型,可能很難游刃有余的去使用 TypeScript,而且最近 TypeScript 發(fā)布了 4.0 的版本新增了更多功能,想要用好它只能不斷的學(xué)習(xí)和掌握它。希望閱讀本文的朋友都能有所收獲,擺脫 AnyScript。

本文轉(zhuǎn)載自微信公眾號(hào)「更了不起的前端」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系更了不起的前端公眾號(hào)。

 

責(zé)任編輯:武曉燕 來源: 更了不起的前端
相關(guān)推薦

2021-10-17 13:10:56

函數(shù)TypeScript泛型

2020-06-12 09:20:33

前端Blob字符串

2020-07-28 08:26:34

WebSocket瀏覽器

2020-08-05 12:17:00

C語言代碼分配

2022-08-10 09:03:35

TypeScript前端

2011-09-15 17:10:41

2022-10-13 11:48:37

Web共享機(jī)制操作系統(tǒng)

2009-12-10 09:37:43

2021-02-01 23:23:39

FiddlerCharlesWeb

2010-08-23 09:56:09

Java性能監(jiān)控

2021-07-05 05:34:10

Typescript語言開發(fā)

2022-11-04 08:19:18

gRPC框架項(xiàng)目

2021-12-29 11:38:59

JS前端沙箱

2021-12-22 09:08:39

JSON.stringJavaScript字符串

2012-11-23 10:57:44

Shell

2015-06-19 13:54:49

2020-08-11 11:20:49

Linux命令使用技巧

2020-03-16 16:20:03

less查看文件Linux

2024-09-11 16:21:09

2025-03-19 09:46:45

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)