複合型別Union & Intersection - iT 邦幫忙

文章推薦指數: 80 %
投票人數:10人

而A 交集B 的集合結果為: { 2, 4 }. 那以上的定義跟TypeScript 的複合型別有什麼差別? 首先筆者先從最簡單的方向開始,也就是我們常看到的聯集 union :. 第11屆iThome鐵人賽 DAY 17 2 ModernWeb 讓TypeScript成為你全端開發的ACE!系列第 17篇 Day17.機動藍圖・複合型別X型別複合-TypeScriptUnion&Intersection 11th鐵人賽 javascript typescript js front-end MaxwellAlexius 2019-09-2715:28:402507瀏覽 閱讀本篇文章前,今天什麼都不用想! 直接進入正文,快看下面! 筆者就直接讓油門繼續摧下去~正文開始! 筆者O.S.:今天又是數學時間,要學好程式可真不簡單,但學好數學可以應用在程式上,何嘗不可? 複合型別Union&Intersection 數學意義的集合V.S.複合型別 基本上,筆者發現有些在學TypeScriptunion與intersection的過程,有人會以為,或者是有這樣的誤解: TypeScriptunion與intersection跟數學上對於集合的聯集與交集的定義是一樣的 很抱歉,以上那句話——大・錯・特・錯——所以被筆者狠狠地劃上了很大的刪節線! 筆者有點想把取這個很讓人誤解的名詞的人給推入火坑。

以下就是踢爆這個誤區的推論時間! 數學定義上的聯集(Union)與交集(Intersection)的概念,通常會有簡單的VennDiagram來展示,我們有集合A與B呈現如圖一: 圖一:集合A的範圍為左方橘色圈圈部分;集合B的範圍為右方藍色圈圈部分 A聯集B(又稱A與B的Union)為A與B包含在一起的範圍——其中,聯集過後元素不可重複,這是集合本身的特性。

而A交集B(又稱A與B的Intersection)為A與B重疊的範圍。

假設A集合包含以下元素:{1,2,3,4} 假設B集合包含以下元素:{2,4,6,8} 其中A聯集B的集合結果為:{1,2,3,4,6,8} 而A交集B的集合結果為:{2,4} 那以上的定義跟TypeScript的複合型別有什麼差別? 首先筆者先從最簡單的方向開始,也就是我們常看到的聯集union: 如果按照數學的概念推理的話,A為number與string的聯集,也就是說UnionSet1可以是number或string。

不過筆者再舉下個例子: 讀者可能覺得:“作者是想表達什麼?這麼簡單的事情:UnionSet2可以是UserInfo1或是UserInfo2啊,因為UserInfo1與UserInfo2都是屬於UnionSet2的範疇。

” 好,請注意這句話: UnionSet2可以是UserInfo1或是UserInfo2 那根據數學推理,照理來說應該只會有這三種組合: 以上就留給讀者進行驗證,TypeScript檢測結果不會出錯。

好的,那麼以下這些結果,根據強制性的數學推理:就算某變數被註記為UnionSet2,而該變數裡的值已經完全滿足UserInfo1或UserInfo2其中一種型別,若另一個型別若完全不滿足的話,理應來說TypeScript要發出錯誤警訊。

(以下程式碼檢測結果如圖二) 圖二:理所當然,第一個案例一定錯,因為既不滿足UserInfo1也不滿足UserInfo2;然而後續的例子,只要至少其中一個型別被判定滿足,不管其他型別有沒有完整補齊,TypeScript認為無所謂 筆者舉一個更諷刺的例子:空集合。

(EmptySet) 讀者一看就知道這一定會出錯(讀者可以自行驗證),因為什麼都不滿足。

然而,筆者必須請讀者回憶一下:“集合論裡,空集合不也是屬於任何聯集過後的集合中的元素嗎?”——這個機制就很像原生JS裡還是必須要有代表空值的undefined或代表空這個概念的值的null。

(當然啦,總是會有開發者閒null跟undefined這兩種東西比較起來還是很蠢,不過這裡筆者沒有什麼太大的異義) 藉由以上的試驗,TypeScript的union型別完全不符合數學理論所預期的規則。

綜觀TypeScript已經出現很久了,使用複合型別的過程中,開發者們在認為理所當然的應用情境下違反了以前學過的最基礎的數學原理,甚至也沒意識到這樣的嚴重性,造成錯誤的觀念亂散播出去。

(一開始就對數學家的專業不尊重,遑論尊不尊重軟體開發者的專業) 再來想一下另一種案例,數學的交集跟TypeScript的intersection有沒有差別。

然而筆者不得不說,這裡數學定義的交集又跟TS的intersection完全背道而馳!請讀者想想看,以下的案例: 根據UserInfo1跟UserInfo2各自的型別格式,有沒有認真想過它們有何交集點? name與age以及hasPet與ownsMotorcycle的組合——兩組屬性的集合中,完全沒有交集! 偏偏在TypeScript裡——交集intersection的用法是:將兩個可以為型別或介面的組合裡的格式進行結合的概念。

也就是說,如果我們註記IntersectionSet在某變數B上,該變數B必須實踐出name、age、hasPet跟ownsMotorcycle這四種屬性!否則會出錯呢。

(以下程式碼檢驗結果如圖三) 圖三:屬性缺一不可啊! 在這裡,筆者必須向讀者澄清,這怎麼能說是交集呢? 筆者認為,我們應該換個想法——TypeScript的union與intersection的原理沒有跟數學的集合論符合,但倒是符合另一個模型!讀者想得到嗎? 布林代數的邏輯(BooleanLogic)! 學了那麼久的And與Or邏輯,應該很明顯的:|是我們常看到的OR的概念;&則是我們常看到的AND的概念。

想想看,剛剛使用union時的情境: 將UserInfo1跟UserInfo2進行union--把它轉換成:將UserInfo1和UserInfo2OR起來之後是不是邏輯通順很多? 你可以選擇只要符合UserInfo1要求的型別或介面格式 或(OR)你可以選擇只要符合UserInfo2要求的型別或介面格式 但你也可以全部都符合 **不過就是至少一個條件一定要滿足,否則出錯!**因此空集合概念在這裡也不覆存在,會被認定是錯的,因為OR邏輯本來就是建立在其中一方符合的條件下才能滿足的。

對比使用intersection時: 將UserInfo1跟UserInfo2進行intersection--把它轉換成:將UserInfo1與UserInfo2AND起來之後是不是邏輯通順很多? 你必須符合UserInfo1和(AND)UserInfo2的型別或介面格式 只要少了一個屬性就會出錯,完全符合AND邏輯的真諦 當初取union跟intersection這兩個名稱的研發者,不是數學概念不好,就是亂用取錯名,簡直是誤導群眾、誤導開發者。

重點1.複合型別的基礎法則FundamentalLawofTSIntersection&Union 複合型別(intersection與union)在TypeScript的運作邏輯完全不等於數學裡集合論的定義。

相對地,複合型別的概念反而是跟布林邏輯的概念符合! 重點1.都已經用『法則』這兩個字形容了,應該可以提醒讀者這個概念的重要性了吧。

重點2.複合型別的語法與規則 假設某型別化名A與B,其中A與B各自可以為型別或者是介面,亦或者都是型別或介面,其中TUnion為A與B的union;而TIntersection為A與B的intersection,則: typeTUnion=A|B; typeTIntersection=A&B; 任意變數C,其中C被註記為TUnion型別,則C的值必須至少符合A或B其中一項型別的完整靜態格式(或實踐出其中一個介面裡的所有功能,如果A或B有存在介面宣告的話)。

任意變數D,其中D被註記為TIntersection型別,則D的值必須完全符合A與B裡所有的型別靜態格式(或實踐出其中一個介面裡的所有功能,如果A或B有存在介面宣告的話)。

接下來就要看一些使用union與intersection會發生的莫名有趣現象。

讀者可能以為union與intersection就只是剛剛講的就結束了~ 沒有喔,還有很多可以講XD,因此筆者才會放在很後面。

原始型別的複合PrimitiveTypesUnion&Intersection 我們剛剛有展示過,原始型別的複合--對A或B型別使用union,其中A不等於B且A和B皆屬於原始型別。

我們都很熟悉A與B進行union,然而我們可曾想過將這A與B型別作intersection的結果? 試想一下:number&string到底是什麼?其實讀者如果有把本系列文章讀熟一定會有答案呢! 筆者突然浮現出的Brainstorming過程: “恩......要能夠同時為number以及string...難道不是空集合嗎?” “可是空集合在TypeScript作者好像沒講到啊...” “世界上真的有既是數字和字串的東西嗎?還是說將數字加兩個引號就可以了?就像這樣:'42'” 筆者回答:當然不存在,但是隱身於所有型別當中的共通點——以下這句話節選自Day10.Never型別: neverisasubtypeofandassignabletoeverytype. 哦~其實這也挺合理的:“既然是不存在的型別交集,最後的結論理應是:例外狀況或不可能的狀況出現,因此推得兩個原始型別的交集結果是never型別”。

(印證的結果如圖四) 圖四:原始型別交集結果就是never 讀者是不是覺得Never型別存在的意義比想像中重要?筆者認為其他的教學資源基本上只是幾百字不到淺淺帶過never型別的語法與用途——但從來沒有把never的真諦傳出去,實是可惜,不然這些特殊型別的行為也挺有趣的。

重點3.原始型別的交集 若TA與TB皆為原始型別,且TA與TB不相同,則兩型別被intersection的結果為never型別: typeMustBeNever=TA&TB; 因此,MustBeNever為never型別 讀者試試看 如果將廣義物件型別跟原始型別進行intersection: 結果判定是never嗎? 如果不是的話,那效果上跟never差不多嗎? 不過讀者仔細想想,這不就跟Never型別那一篇,後面的intersection提到的概念很像嗎? 《Day10.特殊型別X永無止盡-NeverType》之重點2.never型別為所有型別的Subtype 任何型別T(包含never本身)和never進行union,則型別T會吸收掉never型別: typeWontBeNever=T|never; //=>WontBeNever:T 任何型別U(包含never本身)和never進行intersection,則型別U會被never型別強行覆蓋: typeMustBeNever=U&never; //=>MustBeNever:never 其實本篇的重點3.不過就是Never型別篇章重點2.的擴充呢! 型別檢測TypeGuard 其實筆者真的覺得中文好難翻,甚至也懶得去查——寫作到這裡偶然查到原來Generics的翻譯為泛用型別並不是通用型別。

(筆者掩面感到丟臉) 因此,可能會將文章裡的使用詞再進行修改。

不過TypeGuard筆者也很難找到翻譯,只知道可以被形容成型別限縮的概念。

後來覺得『型別檢測』這名詞好像也不錯,乾脆就採用這個名詞——作用依然還是跟型別的限縮有關! 讀者如果看過本系列,這個技巧應該在Anyv.s.Unknown型別篇章遇過一次,那時候是在討論——如果變數被註記或推論為unknown型別時,該變數基本上什麼事情都不能做,這裡原封不動貼上當時的重點: 《Day11.特殊型別X無法無天-Any&UnknownType》之重點2.unknown型別下的變數指派限制 跟any型別相似的地方在於,若變數被unknown型別註記,則該變數可以被任意型別的值指派 若被註記為unknown型別的變數,除了以下情形以外,否則不得將其值指派到任意型別(除了unknown或any)的變數裡: 顯性註記之型別T等同於被指派到的變數之型別T 根據程式的控制流程分析,其unknown型別的推論被*限縮到特定的型別U*致使可以被指派到其他符合型別U條件的變數 其中第二點的最後一句話: “根據程式的控制流程分析,其unknown型別的推論被限縮到特定的型別U致使可以被指派到其他符合型別U條件的變數” 關鍵字是當時筆者說的兩個點(不是人體上的兩個點,筆者的有些朋友會這樣亂想,但這是公眾場合XD):“控制流程分析”與“推論被限縮”。

這很明顯在說明TypeGuard的重點——藉由簡單的判斷敘述來限縮型別的技巧,因此當時候才會有這個範例: 不過筆者這裡不在多做以上程式碼範例的說明,認為需要再複習unknown型別的概念請參考Day11.。

那筆者為何到現在才講TypeGuard? 通常碰到union過後的型別,多數狀況下我們必須主動使用TypeGuard讓TypeScript編譯器不會哀哀叫。

其實之前在介面的函式超載篇章裡的例子舉得不錯,我們重新來看: 當時AddOperation的介面長這樣: 其中我們遇到的狀況是,要實踐AddOperation難免會遇到union的狀況,該函式的參數p1與p2各自可為string或number,因此裡面才會需要if...else...判斷式進行參數型別的判斷。

我們今天來看看另一種狀況。

以上這個ISummation介面是屬於純粹函式格式的介面,也引用了函式超載的概念。

這裡想要達到的效果是--假設某函式F已經實踐了ISummation所訂立的功能規格,則: 其中,若讀者不熟悉...args這種在函式參數裡面的行為(這個叫做匯聚操作子Rest-Operator),請多參考社群們熱心教學的ES6系列文章~ 那麼以下筆者就不客氣地丟出實踐結果。

筆者這一次是第四次編譯本並且測試結果。

讀者如果曉得流程,應該也會直接果斷下tsc然後再去用node執行編譯出來的index.js。

(結果如圖五) 圖五:驗證結果為正確 貼心小提示 有些讀者認為,檢測陣列可以用Array.isArray,這一點也是沒問題的!不過記得要在tsconfig.json裡進行微調: { "compilerOptions":{ /*略...*/ "lib":["dom","es2015"], /*略...*/ } } 有關於編譯器設定將會在《戰線擴張》系列進行介紹,筆者已經確定那時候是30天以後囉~ 這裡筆者想強調的重點是——通常檢測原始型別都是為用typeof這個關鍵字。

`typeofvalue==='' 而通常廣義物件或類別(Class)建造出來的物件則是會用instanceof這個關鍵字: someObjectinstanceofObjectBelongingClass 最常遇到的應該是諸如此類的問題:如何寫限縮型別的判斷式。

重點4.限縮型別的技巧-型別檢測TypeGuard 若想要過濾出純原始型別的值的話,使用typeof操作子 若想要過濾出廣義物件型別的值的話,使用instanceof判斷操作子,並填上屬於該物件型別所屬的類別 其他方式,譬如Array.isArray可以檢測陣列 小結 今天總算了結複合型別,筆者實在是感到溫馨。

(讀者看到篇幅大小感到煩躁) 筆者接下來要進行的是TypeScriptClass也就是類別部分的介紹啦~ 筆者這邊再次強調:你不需要懂ES6Class,筆者幫你建立這方面的基礎,因為基本上學完TypeScriptClass就等於你會了ES6Class大部分的內容~ 而類別的部分也是為了鋪陳本系列後續的重頭戲必備的知識呀! 留言1 追蹤 檢舉 上一篇 Day16.機動藍圖・介面與型別X混用與比較-TypeScriptInterfaceV.S.Type 下一篇 Day18.機動藍圖・類別宣告X藍圖設計-TypeScriptClass 系列文 讓TypeScript成為你全端開發的ACE! 共51篇 目錄 RSS系列文 訂閱系列文 341人訂閱 47 Day47.通用武裝・泛型應用X結合ES2015+-TypeScriptGenericswithES2015+Features 48 Day48.通用武裝・非同步概念X脫離巢狀地獄-TypeScriptGenericswithAsynchronousProgrammingI.PromiseChain 49 Day49.通用武裝・非同步迭代X無窮地惰性求值-TypeScriptGenericswithAsynchronousProgrammingII.ES6Generators 50 Day50.通用武裝・非同步函式X非同步程序的同步化-TypeScriptGenericswithAsynchronousProgrammingIII.AsyncFunctions 51 Day50+用了會上癮的TypeScript新功能-EasilyAddictedNewFeaturesinTypeScript 完整目錄 1則留言 0 zaq159881 iT邦新手5級‧ 2020-09-1214:52:28 感謝大大分享XD 最近剛好看到union以及intersection的地方 想說怎麼跟我原先的認知不同 如果從語法上的&以及|來看 用邏輯布林去理解時的確清楚許多了~ 回應 1 檢舉 MaxwellAlexius iT邦新手5級‧ 2020-09-1401:29:08 檢舉 對,基本上union跟intersection我認為是當初訂規則的人對數學有某種程度誤用,這個關係應該用布林邏輯表示會很適合唷 對,基本上union跟intersection我認為是當初訂規則的人對數學有某種程度誤用,這個關係應該用布林邏輯表示會很適合唷 修改 登入發表回應 我要留言 立即登入留言 iT邦幫忙鐵人賽 參賽組數 1087組 團體組數 52組 累計文章數 20477篇 完賽人數 572人 鐵人賽最新文章 .NetCoreWebApi_筆記21_Swagger及OpenAPI介紹與配置使用方式_API管理與測試探討 .NetCoreWebApi_筆記20_api結合ADO.NET資料庫操作part8_新聞文章查詢 .NetCoreWebApi_筆記19_api結合ADO.NET資料庫操作part7_新聞文章的編輯更新與刪除 .NetCoreWebApi_筆記18_api結合ADO.NET資料庫操作part6_新聞文章表格陳列查詢 .NetCoreWebApi_筆記17_api結合ADO.NET資料庫操作part5_新聞文章新增_新聞類別元素透過API綁定方式 [Bonus系列]-使用useCallback&useMemo的正確時機是什麼? 大盤到底能不能攻上一萬八?? gotodie?那個goto到底能不能用啊? 2021/12/12更新 予焦啦!一夢終須醒...... 前往鐵人賽 技術推廣專區 [Day2]抓取每日收盤價 [Day1]基本工具安裝 利用python取得永豐銀行API的Nonce [Day03]tinyML開發板介紹 永豐金融API測試員 [Day01]在享受tinyML這道美食之前 [Day3]使用ta-lib製作指標 [Day4]函數打包與買進持有報酬率試算 計算API所需要的參數:HashID 計算API所需要的參數:IV 前往鐵人賽 熱門問題 公司想要架設一個網購物站,但是不知道要怎麼規劃預算 正要準備開始上CCNA… 無網際網路時,請問兩台電腦如何‘無線’遠端連線 遠端監控電腦效能 如何設定一台電腦,有線網卡走公司內網,無線網卡走外網,同時運行? mssql資料庫搬移時,怎樣出所有需要的資料會比較好? 請問有在做關貿資料串接的資訊公司嗎? NAS、雲端儲存空間、隨身硬碟差別在那?怎麼選? 照片辨識比對軟體開發 微軟授權疑問 IT邦幫忙 站方公告 【2021iThome鐵人賽】登登登!究竟獎落誰家,2021iThome鐵人賽得獎名單正式揭曉 熱門tag 看更多 13th鐵人賽 12th鐵人賽 11th鐵人賽 鐵人賽 2019鐵人賽 2018鐵人賽 javascript 2017鐵人賽 windows php python windowsserver linux c# 程式設計 資訊安全 css vue.js sql 分享 熱門回答 正要準備開始上CCNA… 遠端監控電腦效能 如何設定一台電腦,有線網卡走公司內網,無線網卡走外網,同時運行? NAS、雲端儲存空間、隨身硬碟差別在那?怎麼選? 無網際網路時,請問兩台電腦如何‘無線’遠端連線 照片辨識比對軟體開發 Linux主機抓取Windows主機檔案 公司想要架設一個網購物站,但是不知道要怎麼規劃預算 mssql資料庫搬移時,怎樣出所有需要的資料會比較好? DefaultDomainPolicy密碼複雜度 熱門文章 [Bonus系列]-使用useCallback&useMemo的正確時機是什麼? 為了轉生而點技能-JavaScript,day23(Promise介紹 .NetCoreWebApi_筆記17_api結合ADO.NET資料庫操作part5_新聞文章新增_新聞類別元素透過API綁定方式 12.MYSQL淺談NULL 數位簽章(digitalsignature) 【徵才/台北信義區】美商全職駐點MIS工程師 D9.學習基礎C、C++語言 7.MYSQL表格程式語法 javascript變數與運算子2 javascript流程控制-判斷式1 一週點數排行 更多點數排行 海綿寶寶(antijava) 居然解出來了(partyyaya) ㊣浩瀚星空㊣(yoching) 小山丘(a243318490) raytracy(raytracy) ccenjor(ccenjor) 純真的人(jer5173) PPTaiwan(Pochengtaiwan) Gary(mosbbs) souda(souda) × At 輸入對方的帳號或暱稱 Loading 找不到結果。

標記 {{result.label}} {{result.account}} 關閉



請為這篇文章評分?