<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          盤點 TypeScript 中我們常用的那些神奇符號

          共 9425字,需瀏覽 19分鐘

           ·

          2021-09-12 08:59


          點擊上方 三分鐘學(xué)前端,關(guān)注公眾號

          回復(fù)交流,加入前端編程面試算法每日一題群


          面試官也在看的前端面試資料

          ?. 可選鏈(Optional Chaining)

          ES11(ES2020)新增的特性,TypeScript 3.7 支持了這個特性

          我們在 為什么要使用 TypeScript?TypeScript 相對于 JavaScript 的優(yōu)勢是什么?中提到 TypeScript 與標準同步發(fā)展,并推進了很多 ECMAScripts 語法提案,比如可選鏈操作符( ?. )、空值合并操作符( ?? )、Throw 表達式、正則匹配索引等,所以,這里介紹的符號大部分都是 ECMAScripts 規(guī)范的,TypeScript 特有的只有 ?:! 、& 、 |

          可選鏈可讓我們在查詢具有多層級的對象時,不再需要進行冗余的各種前置校驗:

          var info = user && user.info

          又或是這種

          var age = user && user.info && user.info.getAge && user.info.getAge()

          很容易命中 Uncaught TypeError: Cannot read property...

          用了 Optional Chaining ,上面代碼會變成

          var info = user?.info
          var age = user?.info?.getAge?.()

          TypeScript 在嘗試訪問 user.info 前,會先嘗試訪問 user ,user 既不是 null 也不是 undefined 才會繼續(xù)往下訪問,如果usernull 或者 undefined,則表達式直接返回 undefined

          即可選鏈是一種先檢查屬性是否存在,再嘗試訪問該屬性的運算符 ( ?.

          目前,可選鏈支持以下語法操作:

          obj?.prop
          obj?.[expr]
          arr?.[index]
          func?.(args)

          ?? 空值合并運算符(Nullish coalescing Operator)

          ES12(ES2021)新增的特性,TypeScript 3.7 支持了這個特性,當(dāng)左側(cè)的操作數(shù)為 null 或者 undefined 時,返回其右側(cè)操作數(shù),否則返回左側(cè)操作數(shù)。

          // {
          //   "level": null
          // }
          var level1 = user.level ?? '暫無等級' // level1 -> '暫無等級'
          var level2 = user.other_level ?? '暫無等級' // level1 -> '暫無等級'

          與邏輯或操作符(||) 不同,|| 會在左側(cè)操作數(shù)為 falsy 值(例如,''0)時返回右側(cè)操作數(shù)。也就是說,如果使用 || 來為某些變量設(shè)置默認值,可能會遇到意料之外的行為:

          // {
          //   "level": 0   
          // }
          var level1 = user.level || '暫無等級' // level1 -> 暫無等級
          var level2 = user.level ?? '暫無等級' // level2 -> 0

          ?: 可選參數(shù)和屬性

          TypeScript 特有的,在 TypeScript 2.0 支持了這個特性,可選參數(shù)和屬性會自動把 undefined 添加到他們的類型中,即使他們的類型注解明確不包含 undefined 。例如,下面兩個類型是完全相同的:

          // 使用--strictNullChecks參數(shù)進行編譯
          type T1 = (x?: number) => string              // x的類型是 number | undefined
          type T2 = (x?: number | undefined) => string  // x的類型是 number | undefined

          在TypeScript里,我們使用 ?: 最多的情況是在接口中,通常:

          interface Point {
            x: number;
            y: number;
          }

          let point: Point
          point = {
            x: 1,
            y: 2
          }

          其中 point 中的兩個屬性 x 、 y 都是必須的,如果賦值時缺少任意一個就會報錯:

          point = {
            x: 1
          }
          // Property 'y' is missing in type '{ x: number; }' but required in type 'Point'.

          但接口里的屬性不全都是必需的。有些是只在某些條件下存在,或者根本不存在。所以,這里就需要可選屬性( ?. ),即屬性是可選的

          interface Point {
            x: number;
            y: number;
            z?: number// 可選屬性
          }

          let point: Point
          point = {
            x: 1,
            y: 2
          }

          在 TypeScript 有兩個內(nèi)置的工具泛型可以幫助我們處理接口的可選操作:

          • Partial :把接口中的所有屬性變成可選的
          • Required :將接口中所有可選的屬性改為必須的

          Partial

          Partial 的作用即把類型中的所有屬性變成可選的

          /**
           * Make all properties in T optional
           */

          type Partial<T> = {
              [P in keyof T]?: T[P];
          }

          例如:

          interface Point {
            x: number;
            y: number;
          }

          type PartialPoint = Partial<Point>

          // PartialPoint 相當(dāng)于:
          // type PartialPoint = {
          //     x?: number;
          //     y?: number;
          // }
          // 所有屬性均可選

          它具體是如何實現(xiàn)的喃?

          首先了解 keyofkeyof 指的是把我們一個對象里面的鍵值對里的鍵( key )一一羅列出來,并把它們聯(lián)合起來形成一種聯(lián)合類型:

          interface Point {
            x: number;
            y: number;
          }

          type PointKeys = keyof Point // "x" | "y"

          in 是遍歷的作用,P in keyof Tkeyof T 進行一個個遍歷并且每個都單獨拿出來生成新的 "鍵值對"

          所以:

          // Partial 語法
          // type Partial<T> = {
          //   [P in keyof T]?: T[P];
          // };

          interface Point {
            x: number;
            y: number;
          }

          type PartialPoint = Partial<Point>

          // 第一步↓
          type PartialPoint = {
            [P in 'x' | 'y']?: Point[P];
          }

          // 第二步↓
          type PartialPoint = {
            x?: Point["x"];
            y?: Point["y"];
          }

          // 最終↓
          type PartialPoint = {
            x?: number;
            y?: number;
          }

          因此,實現(xiàn)了 Partial 的效果

          Required

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

          type Required<T> = {
            [P in keyof T]-?: T[P];
          }

          例如:

          interface Point {
            x?: number;
            y?: number;
          }

          type RequiredPoint = Required<Point>

          // RequiredPoint 相當(dāng)于:
          // type RequiredPoint = {
          //     x: number;
          //     y: number;
          // }
          // 所有屬性均必須

          ! 非空斷言操作符

          TypeScript 特有的,在 TypeScript 2.0 支持了這個特性,在上下文中當(dāng)類型檢查器無法斷定類型時,一個新的后綴表達式操作符 ! 可以用于斷言操作對象是非 null 和非 undefined 類型的。具體而言,運算 x! 產(chǎn)生一個不包含 nullundefinedx 的值。

          function sayHello(hello: string | undefined{
            const hi1 = hello!.toLowerCase() // OK
            const hi2 = hello.toLowerCase() // Error: Object is possibly 'undefined'
          }

          僅僅只是騙過了編譯器,當(dāng)你調(diào)用 sayHello() 依然會報錯,這樣使用是因為你已經(jīng)斷言了 hello 一定是 string

          let root: (HTMLElement | null) = document.getElementById('root')
          // 非空斷言操作符--> 這樣寫只是為了騙過編譯器,防止編譯的時候報錯,但打包后的代碼可能還是會報錯
          root!.style.color = 'red'

          非空斷言操作符 與 類型守衛(wèi)

          類型守衛(wèi)用于確保該類型在一定的范圍內(nèi),常用 typeof 、 instanceofin

          function sayHello(hello: string | undefined{
              if(typeof hello === 'string') {
                  const hi = hello.toLowerCase() 
              }
          }

          但如果你這樣寫:

          function sayHello(hello: string | undefined{
            const isSay = typeof hello === 'string'
            if(isSay) {
              const hi1 = hello.toLowerCase() // Error: Object is possibly 'undefined'.
              const hi2 = hello!.toLowerCase() // OK
            }
          }

          就會報錯,即使 isSay 被分配到了類型守衛(wèi)值,TypeScript 也只會丟失該信息。所以我們一般會 const hi = hello!.toLowerCase() 加上非空斷言操作符

          但 TypeScript 4.4 RC 會修復(fù)這個問題,如果你遇到這個問題,可升級到 TypeScript 4.4 版本后

          _ 數(shù)字分隔符(Numeric separators)

          ES12(ES2021)新增的特性,TypeScript 2.7 就已經(jīng)支持了這個特性, 這個特性允許用戶在數(shù)字之間使用下劃線_來對數(shù)字分組。

          const million = 1_000_000
          const phone = 173_1777_7777
          const bytes = 0xFF_0A_B3_F2
          const word = 0b1100_0011_1101_0001

          需要注意的是以下函數(shù)是不支持分隔符:

          • Number()
          • parseInt()
          • parseFloat()
          const million = '1_234_567'

          Number(million) 
          // NaN

          parseInt(million) 
          // 1

          parseFloat(million)
          // 1

          ** 指數(shù)操作符

          ES7(ES2016)新增的特性

          2**5 // 32

          & 交叉類型(Intersection Types)

          在 TypeScript 中,交叉類型是將多個類型合并為一個類型,我們可以通過 & 把現(xiàn)有的多種類型疊加到一起成為一種類型,它包含了所需的所有類型的特性

          type PointX = {
           x: number;
          }

          type Point =  PointX & {
           y: number;
          }

          let point: Point = {
              x: 1,
              y: 2
          }

          如果多個類型中存在相同的屬性喃?

          type PointX = {
           x: number;
           z: string;
          }

          type Point =  PointX & {
           y: number;
              z: number;
          }

          let point: Point = {
              x: 1,
              y: 2,
              z: 3// Type 'number' is not assignable to type 'never'.

          這里 z 為什么會是 never 類型喃?因為 string & number 的值是永不存在的值,即 never

          type PointX = {
           x: number;
           z: {x: string};
          }

          type Point =  PointX & {
           y: number;
             z: {z: number};
          }

          let point: Point = {
              x: 1,
              y: 2,
              z: { 
                x: '1',
                z: 2
              },
          }

          而這樣是可以的,所以,即多個類型合并為一個交叉類型時,如果多個類型間存在同名基礎(chǔ)類型屬性時,合并后的同名基礎(chǔ)類型屬性為 never ,如果同名屬性均為非基礎(chǔ)類型,則可以成功合并

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

          聯(lián)合類型表示一個值可以是幾種類型之一,用豎線( |)分隔每個類型,所以 number | string | boolean 表示一個值可以是 number, string,或 boolean

          let user: string | number | boolean = 'an'

          聯(lián)合類型通常與 nullundefined 一起使用:

          const helloName = (name: string | undefined) => {
            /* ... */
          };

          你也可以這么用:

          type Hello = 'say' | 'kiss' | 'smile';


          來源 | https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/529

          最后

          歡迎關(guān)注「三分鐘學(xué)前端」,回復(fù)「交流」自動加入前端三分鐘進階群,每日一道編程算法面試題(含解答),助力你成為更優(yōu)秀的前端開發(fā)!

          號內(nèi)回復(fù):

          網(wǎng)絡(luò)」,自動獲取三分鐘學(xué)前端網(wǎng)絡(luò)篇小書(90+頁)
          JS」,自動獲取三分鐘學(xué)前端 JS 篇小書(120+頁)
          算法」,自動獲取 github 2.9k+ 的前端算法小書
          面試」,自動獲取 github 23.2k+ 的前端面試小書
          簡歷」,自動獲取程序員系列的 120 套模版
          》》面試官也在看的前端面試資料《《
          “在看和轉(zhuǎn)發(fā)”就是最大的
          瀏覽 35
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲无码成人在线播放 | 精品秘 一区二三区在线男奴 | 青青草在线撸 | 古典武侠区伊人一区人妻在线 | 黄色片大女人吃大鸡巴老头子日大逼逼 |