<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>

          什么是鴨子??類型?

          共 3960字,需瀏覽 8分鐘

           ·

          2021-12-14 19:18

          大家好,我是 ConardLi,今天我們一起來看一個 TypeScript 中一個有趣的知識點 - 鴨子類型(Duck Typing)。

          什么是鴨子類型

          鴨子類型是很多面向?qū)ο螅?code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(145, 109, 213);font-weight: bolder;background-image: none;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;">OOP)語言中的常見做法。它的名字來源于所謂的“鴨子測試”:

          當(dāng)看到一只鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那么這只鳥就可以被稱為鴨子。

          我們不用關(guān)心鴨子的定義是什么,只要符合我們通常意義上的認知,那么他就是這個物體。在 TypeScript 中,只要對象符合定義的類型約束,那么我們就可以視為他是。

          鴨子類型 通常用于需要處理一系列不同數(shù)據(jù)的代碼中,我們可能不知道調(diào)用者要傳遞哪些參數(shù)。在一些 switch 語句或復(fù)雜的 if/else 判斷中,通常是 鴨子類型 可能派上用場的地方。

          為什么需要鴨子類型

          在一些動態(tài)語言中,鴨子類型的常見用法就是假設(shè)給定值符合我們預(yù)期的,你可以先嘗試執(zhí)行一個操作,然后我們再去處理不符合預(yù)期的情況下的異常。比如在下面這段 Python 代碼中:

          from?typing?import?Any

          def?is_duck(value:?Any)?->?bool:
          ??try:
          ????value.quack()
          ????return?True
          ??except?(Attribute,?ValueError):
          ????return?False

          這段代碼寫的很蠢,不過表達的意思挺明確的,你通過調(diào)用傳入?yún)?shù)的 .quack() 方法檢查它是否可以嘎嘎叫,如果它嘎嘎叫了,就返回 true ,如果它沒有這個方法,異常就會被捕獲,則返回 false。

          Python 中,try-except 是一種常見的寫法,它也被很多庫(比如hasattr)廣泛使用。

          相比之下,在 JavaScript 中,try-catch 則存在很多限制 — 你既不能根據(jù)拋出異常的原型定義不同的 catch 塊,也不能確定拋出的到底是不是一個異常實例。

          所以,我們在處理異常的時必須更加謹慎,所以在 JavaScriptTypeScript 中我們要做這樣的判斷可能有點逆向思維。通常的做法可能是這樣的:

          function?isDuck(value)?{
          ????return?!!(
          ????????value?&&
          ????????typeof?value?===?'object'?&&
          ????????typeof?Reflect.get(value,?'quack')?===?'function'
          ????);
          }

          在上面的函數(shù)中,我們做了下面幾個判斷:

          • 檢查參數(shù) value 是不是為空
          • 檢查參數(shù) value 是否為 object 類型
          • 通過 Reflect.get 方法更安全安全地判斷 quack 是不是一個函數(shù)

          你可能對這種代碼再熟悉不過了,畢竟在 JavaScript 代碼里這種布爾判斷遍地都是。

          如果用 TypeScript 的話寫法可能就不一樣了,參數(shù) value 可能是只鴨子,但 IDEJavaScript 解析器都不知道鴨子是啥。在 TypeScript 中,我們可以把鴨子定義成一個類型:

          interface?Duck?{
          ????quack():?string;
          }

          interface?Cat?{
          ????miao():?string;
          }


          function?isDuck(value:?Cat?|?Duck)?{
          ????return?!!(
          ????????value?&&
          ????????typeof?value?===?'object'?&&
          ????????typeof?value.quack?===?'function'
          ????);
          }

          這里我們在參數(shù) value 的類型中告訴 TypeScript 解析器,它可能是只鴨子也可能是只貓,你需要再函數(shù)體的邏輯中再做進一步判斷。

          但是,解析器可能沒我們想象中的那么聰明,這里會報錯,因為他還是不能確定 value 到底是只鴨子還是只貓,所以無法確定 quack 函數(shù)是不是存在。

          下面的寫法就可以幫我們解決這個問題:

          interface?Duck?{
          ????quack():?string;
          }

          function?isDuck(value:?unknown):?value?is?Duck?{
          ????return?!!(
          ????????value?&&
          ????????typeof?value?===?'object'?&&
          ????????typeof?value.quack?===?'function'
          ????);
          }

          注意,isDuck 的返回值類型中使用了 is 關(guān)鍵字,這在 TypeScript 中被叫做類型謂詞(type predicates),類型謂詞是一個返回布爾值的函數(shù),可以用來做類型保護;

          類型保護是可執(zhí)行運行時檢查的一種表達式,用于確保該類型在一定的范圍內(nèi)。換句話說,類型保護可以保證一個字符串是一個字符串,盡管它的值也可以是一個數(shù)字。

          實際上它就是告訴 TypeScript 編譯器給定的值是就是我們給定的那個類型。

          簡單的說,就是告訴編譯器這個可能是鴨子的東西就是一只鴨子。

          用法示例 recursiveResolve

          鴨子類型的一個方便用法是當(dāng)你的代碼可能接受 Promise 或者 非Promise 時來幫我們進行更優(yōu)雅的判斷。

          假設(shè)我們創(chuàng)建了一個自定義方法來遞歸遍歷對象,解析可能嵌套在里面的任何 Promise,下面就是一個很好的用法:


          function?isRecord<T?=?any>(value:?unknown):?value?is?Record<string?|?symbol,?T>?{
          ????return?!!(value?&&?typeof?value?===?'object');
          }

          function?isPromise<T?=?unknown>(value:?unknown):?value?is?Promise<T>?{
          ????return?isRecord(value)?&&?Reflect.has(value,?'then');
          }

          async?function?recursiveResolve<T>(
          ????parsedObject:?Record<string,?any>,
          ):?Promise<T>?
          {
          ????const?output?=?{};
          ????for?(const?[key,?value]?of?Object.entries(parsedObject))?{
          ????????const?resolved:?unknown?=?isPromise(value)???await?value?:?value;
          ????????if?(isRecord(resolved))?{
          ????????????Reflect.set(output,?key,?await?recursiveResolve(resolved));
          ????????}?else?{
          ????????????Reflect.set(output,?key,?resolved);
          ????????}
          ????}
          ????return?output?as?T;
          }

          我們在上面定義了兩個類型謂詞 - isPromiseand、isRecord,它們都接受一個可選的泛型參數(shù),這樣它們就能被復(fù)用。然后我們就可以在 recursiveResolve 函數(shù)中使用它們了,并且開銷是很小的,在整個函數(shù)中都能正確推斷輸入。

          小技巧 - 通用類型保護

          上面的判斷可能在我們的代碼中是個很常見的用法,如果我們需要判斷的類型有很多,為每個類型都實現(xiàn)一個這樣的類型保護函數(shù)還挺麻煩的,所以我們可以稍微做個變形來封裝一個更通用的類型保護函數(shù):

          export?const?isOfType?=?(
          ??varToBeChecked:?any,
          ??propertyToCheckFor:?keyof?T
          ):?varToBeChecked?is?T?=>
          ??(varToBeChecked?as?T)[propertyToCheckFor]?!==?undefined;

          你可以向這樣用:

          interface?code秘密花園?{
          ????好文章:?string;
          }

          if?(isOfType(公眾號,?'好文章'))?{
          ??console.log('這里有好文章,這里是code秘密花園!');
          }?else?{
          ??console.log("這里不是code秘密花園!");
          }

          參考

          • https://javascript.plainenglish.io/what-is-duck-typing-in-typescript-c537d2ff9b61
          • https://rangle.io/blog/how-to-use-typescript-type-guards/

          往期推薦



          解密初、中、高級程序員的進化之路(前端)


          程序員一定會有35歲危機嗎?


          近 20k Star的項目說不做就不做了,但總結(jié)的內(nèi)容值得借鑒


          但凡早知道這28個網(wǎng)站,都不至于學(xué)得那么不扎實





          如果你覺得這篇內(nèi)容對你挺有啟發(fā),我想邀請你幫我三個小忙:

          1. 點個「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點在看,都是耍流氓 -_-)

          2. 歡迎加我微信「huab119」拉你進技術(shù)群,長期交流學(xué)習(xí)...

            關(guān)注公眾號「前端勸退師」,持續(xù)為你推送精選好文,也可以加我為好友,隨時聊騷。



          點個在看支持我吧,轉(zhuǎn)發(fā)就更好了

          如果覺得這篇文章還不錯,來個【轉(zhuǎn)發(fā)、收藏、在看】三連吧,讓更多的人也看到~

          瀏覽 94
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  久草综合在线 | 免费在线看片黄在线观看 | 亚洲天堂高清 | 日日爱网| 免费黄色视频网站观看 |