[{"data":1,"prerenderedAt":481},["ShallowReactive",2],{"content:\u002F2023\u002Ftypescript\u002Fts-4":3,"surround:\u002F2023\u002Ftypescript\u002Fts-4":470},{"id":4,"title":5,"body":6,"categories":446,"date":449,"description":15,"draft":450,"extension":451,"image":452,"meta":453,"navigation":455,"path":456,"permalink":452,"published":452,"readingTime":457,"recommend":452,"references":452,"seo":462,"sitemap":463,"stem":466,"tags":467,"type":468,"updated":452,"__hash__":469},"content\u002Fposts\u002F2023\u002Ftypescript\u002Fts-4.md","TS 中的类型运算：联合类型",{"type":7,"value":8,"toc":428},"minimark",[9,16,19,24,27,34,86,90,99,111,117,121,126,132,146,150,156,162,195,199,205,211,226,230,236,239,245,249,256,262,265,268,271,277,282,289,313,322,328,331,338,344,348,354,373,376,393,399,410,413,419],[10,11,12],"p",{},[13,14,15],"strong",{},"TS = JS + 类型系统",[10,17,18],{},"JS 可以对值进行加减运算，如果把 TS 的类型系统当作一门语言，TS 也同样可以对类型进行各种运算",[20,21,23],"h2",{"id":22},"联合类型并集","联合类型（并集）",[10,25,26],{},"英文全称：union types",[10,28,29],{},[30,31],"img",{"alt":32,"src":33},"","\u002Fimages\u002Ftypescript\u002Fts-4\u002Fts_4_01.png",[10,35,36,40,41,44,45,48,49,51,52,55,56,59,60,63,64,66,67,70,71,73,74,40,77,44,79,82,83,85],{},[37,38,39],"code",{"code":39},"type A = { name: string }"," 表示 ",[37,42,43],{"code":43},"name"," 为 ",[37,46,47],{"code":47},"string"," 的所有对象\n但不要错误地认为这些对象只有 ",[37,50,43],{"code":43}," 这一个 ",[37,53,54],{"code":54},"key","\n比如对象 ",[37,57,58],{"code":58},"{ name: 'ClariS'; age: 18 }"," 也是属于 ",[37,61,62],{"code":62},"A"," 类型的\n即 ",[37,65,62],{"code":62}," 类型的对象可以有 ",[37,68,69],{"code":69},"age","，也可以没有 ",[37,72,69],{"code":69},"\n同理，",[37,75,76],{"code":76},"type = { age: number }",[37,78,69],{"code":69},[37,80,81],{"code":81},"number"," 的对象，这些对象的 ",[37,84,43],{"code":43}," 可以为空，也可以不为空",[20,87,89],{"id":88},"如何使用联合类型","如何使用联合类型？",[91,92,97],"pre",{"className":93,"code":95,"language":96,"meta":32},[94],"language-ts","const f1 = (a: number | string) => {\n  '既不能把 a 当作 number'\n  '也不能把 a 当做 string'\n  '那么，怎么使用 a 变量呢？'\n}\n","ts",[37,98,95],{"__ignoreMap":32},[10,100,101,102,104,105,107,108],{},"如果不拆开类型，就只能使用 ",[37,103,81],{"code":81}," 和 ",[37,106,47],{"code":47}," 共同拥有的方法或属性，比如 ",[37,109,110],{"code":110},"toString()",[10,112,113,114],{},"因此要使用联合类型，就得先区分类型，也就是进行",[13,115,116],{},"类型收窄(Narrowing)",[20,118,120],{"id":119},"如何进行类型收窄","如何进行类型收窄？",[122,123,125],"h3",{"id":124},"使用-typeof","使用 typeof",[91,127,130],{"className":128,"code":129,"language":96,"meta":32},[94],"const f1 = (a: number | string) => {\n  if (typeof a === 'number') {\n    a.toFixed(2);\n  } else if (typeof a === 'string') {\n    parseFloat(a).toFixed(2);\n  } else {\n    throw new Error('Never do this');\n  }\n};\n",[37,131,129],{"__ignoreMap":32},[10,133,134,137,138,141,142,145],{},[37,135,136],{"code":136},"typeof"," 的局限性：无法区分数组、日期、普通对象以及 ",[37,139,140],{"code":140},"null","（返回值都是 ",[37,143,144],{"code":144},"\"object\"","）",[122,147,149],{"id":148},"使用-instanceof","使用 instanceof",[91,151,154],{"className":152,"code":153,"language":96,"meta":32},[94],"const f1 = (a: Array\u003CDate> | Date) => {\n  if (a instanceof Date) {\n    a.toISOString();\n  } else if (a instanceof Array) {\n    a[0].toISOString();\n  } else {\n    throw new Error('Never do this');\n  }\n};\n",[37,155,153],{"__ignoreMap":32},[10,157,158,161],{},[37,159,160],{"code":160},"instanceof"," 的局限性：",[163,164,165,186],"ul",{},[166,167,168,169,171,172,171,174,171,177,171,180,171,183],"li",{},"不支持基础类型，",[37,170,47],{"code":47},"、",[37,173,81],{"code":81},[37,175,176],{"code":176},"bigint",[37,178,179],{"code":179},"boolean",[37,181,182],{"code":182},"symbol",[37,184,185],{"code":185},"undefined",[166,187,188,189],{},"不支持 TS 独有的类型\n",[91,190,193],{"className":191,"code":192,"language":96,"meta":32},[94],"type Person = { name: string };\n\nconst f1 = (a: Person | Person[]) => {\n  if (a instanceof Person) {\n    \u002F\u002F             ^--- 报错：type 不能用作 value\n  } else {\n    throw new Error('Never do this');\n  }\n};\n",[37,194,192],{"__ignoreMap":32},[122,196,198],{"id":197},"使用-in-操作符","使用 in 操作符",[91,200,203],{"className":201,"code":202,"language":96,"meta":32},[94],"type Person = { name: string };\n\nconst f1 = (a: Person | Person[]) => {\n  if ('name' in a) {\n    a; \u002F\u002F Person\n  } else {\n    a; \u002F\u002F Person[]\n  }\n};\n",[37,204,202],{"__ignoreMap":32},[10,206,207,210],{},[37,208,209],{"code":209},"in"," 操作符的局限性：只适用于部分对象",[10,212,213,214,216,217,219,220,222,223,225],{},"比如当判断的两个对象存在相同的 ",[37,215,54],{"code":54}," （存在相同 ",[37,218,54],{"code":54},"，其中一个对象的 ",[37,221,54],{"code":54}," 可能不存在），或不为键值对形式的对象（比如日期、正则、函数等）时，则无法使用 ",[37,224,209],{"code":209}," 操作符来收窄类型",[122,227,229],{"id":228},"使用-js-中判断类型的函数","使用 JS 中判断类型的函数",[91,231,234],{"className":232,"code":233,"language":96,"meta":32},[94],"const f1 = (a: string | string[]) => {\n  if (Array.isArray(a)) {\n    a.join('\\n').toString();\n  } else if (typeof a === 'string') {\n    parseFloat(a).toFixed(2);\n  } else {\n    throw new Error('Never do this');\n  }\n};\n",[37,235,233],{"__ignoreMap":32},[122,237,238],{"id":238},"使用逻辑",[91,240,243],{"className":241,"code":242,"language":96,"meta":32},[94],"const f1 = (a?: string[]) => {\n  if (a) {\n    a; \u002F\u002F string[]\n  } else {\n    a; \u002F\u002F undefined\n  }\n};\n\nconst f2 = (a: string | number) => {\n  a = 1;\n  a; \u002F\u002F number\n};\n\nconst f3 = (x: string | number, y: string | boolean) => {\n  if (x === y) {\n    x; \u002F\u002F string\n    y; \u002F\u002F string\n  } else {\n    x; \u002F\u002F string | number\n    y; \u002F\u002F string | number\n  }\n};\n",[37,244,242],{"__ignoreMap":32},[122,246,248],{"id":247},"使用类型谓词-is","使用类型谓词 is",[10,250,251,252,255],{},"上述所有的类型收窄方法都是通过 ",[37,253,254],{"code":254},"JavaScript"," 来实现的，有没有区分类型的万全之法？",[91,257,260],{"className":258,"code":259,"language":96,"meta":32},[94],"type Rect = { height: number; width: number };\n\ntype Circle = { center: [number, number]; radius: number };\n\n\u002F\u002F 推荐使用普通函数实现 isRect，而不使用箭头函数\nfunction isRect(x: Rect | Circle): x is Rect {\n  return 'height' in x && 'width' in x;\n}\n\nconst f1 = (a: Rect | Circle) => {\n  if (isRect(a)) { \u002F\u002F `isRect(a)` 判断 a 是不是 Rect\n    a; \u002F\u002F Rect\n  } else {\n    a; \u002F\u002F Circle\n  }\n};\n",[37,261,259],{"__ignoreMap":32},[10,263,264],{},"优点：支持所有 TS 类型\n缺点：需要自己实现，挺麻烦的",[10,266,267],{},"有没有更简单的办法？",[122,269,270],{"id":270},"可辨别联合",[91,272,275],{"className":273,"code":274,"language":96,"meta":32},[94],"interface Circle { kind: \"circle\"; radius: number; }\ninterface Square { kind: \"square\"; sideLength: number; }\ntype Shape = Circle | Square\n\nconst f1 = (shape: Shape) => {\n  if (shape.kind === 'circle') {\n    shape \u002F\u002F Circle\n  } else if (shape.kind === 'square') {\n    shape \u002F\u002F Square\n  } else {\n    shape \u002F\u002F never\n  }\n}\n",[37,276,274],{"__ignoreMap":32},[10,278,279],{},[13,280,281],{},"让复杂类型的收窄变成简单类型的对比",[10,283,284,285,288],{},"对于类型 ",[37,286,287],{"code":287},"T = A | B | C | D | ...","，有以下要求：",[290,291,292,302,307],"ol",{},[166,293,294,297,298,301],{},[37,295,296],{"code":296},"A、B、C、D..."," 有相同属性 ",[37,299,300],{"code":300},"kind"," 或其他",[166,303,304,306],{},[37,305,300],{"code":300}," 的类型是简单类型",[166,308,309,310,312],{},"各类型中的 ",[37,311,300],{"code":300}," 可区分",[10,314,315,316,319,320],{},"则称 ",[37,317,318],{"code":318},"T"," 为",[13,321,270],{},[10,323,324,325,327],{},"一句话总结：具有同名、可辨别的简单类型的 ",[37,326,54],{"code":54}," 的联合类型，称为可辨别联合",[122,329,330],{"id":330},"使用断言",[10,332,333,334,337],{},"可以使用 ",[37,335,336],{"code":336},"as"," 进行强制的类型收缩",[91,339,342],{"className":340,"code":341,"language":96,"meta":32},[94],"interface Circle { kind: \"circle\"; radius: number; }\ninterface Square { kind: \"square\"; sideLength: number; }\ntype Shape = Circle | Square\n\nconst f1 = (shape: Shape) => {\n  (shape as Circle).radius = 4\n}\n",[37,343,341],{"__ignoreMap":32},[20,345,347],{"id":346},"any-等于所有类型的联合吗","any 等于所有类型的联合吗？",[10,349,350,351],{},"这里直接给出结论：",[13,352,353],{},"any 不等于所有类型的联合",[355,356,357],"blockquote",{},[10,358,359,360,363,364,363,367,363,370],{},"注意：这里的所有类型不包括 ",[37,361,362],{"code":362},"never"," ",[37,365,366],{"code":366},"unknown",[37,368,369],{"code":369},"any",[37,371,372],{"code":372},"void",[10,374,375],{},"用反证法可以证明：",[290,377,378,381,387],{},[166,379,380],{},"只要类型发生了联合，就只能使用它们共同拥有的属性或方法（想要使用各自的方法就必须做类型收窄）",[166,382,383,384,386],{},"但是使用 ",[37,385,369],{"code":369}," 之后，不做类型收窄可以使用所有类型的方法",[166,388,389,390,392],{},"说明 ",[37,391,369],{"code":369}," 不是所有类型的联合",[91,394,397],{"className":395,"code":396,"language":96,"meta":32},[94],"const f1 = (a: string | number) => {\n  a.toString();\n  a.toFixed(); \u002F\u002F 报错\n  a.split(); \u002F\u002F 报错\n};\n\nconst f2 = (a: any) => {\n  a.toString();\n  a.toFixed();\n  a.split();\n};\n",[37,398,396],{"__ignoreMap":32},[10,400,401,402,404,405,407,408],{},"且 ts 的绝大部分规则对 ",[37,403,369],{"code":369}," 不生效，但有一种特殊情况，",[37,406,369],{"code":369}," 无法赋值给 ",[37,409,362],{"code":362},[10,411,412],{},"那什么等于所有类型的联合呢？",[91,414,417],{"className":415,"code":416,"language":96,"meta":32},[94],"const f1 = (a: unknown) => {\n  if (a instanceof Date) {\n    a; \u002F\u002F Date\n  }\n};\n\ntype Rect = { height: number; width: number };\ntype Circle = { center: [number, number]; radius: number };\nfunction isRect(x: Rect | Circle): x is Rect {\n  return 'height' in x && 'width' in x;\n}\n\nconst f2 = (a: unknown) => {\n  if (isRect(a)) {\n    a; \u002F\u002F Rect\n  }\n};\n",[37,418,416],{"__ignoreMap":32},[10,420,421,422,424,425,427],{},"可以看出，",[37,423,366],{"code":366}," 是可以收窄到任何类型的，因此，",[37,426,366],{"code":366}," 就是所有类型的联合",{"title":32,"searchDepth":429,"depth":429,"links":430},4,[431,433,434,445],{"id":22,"depth":432,"text":23},2,{"id":88,"depth":432,"text":89},{"id":119,"depth":432,"text":120,"children":435},[436,438,439,440,441,442,443,444],{"id":124,"depth":437,"text":125},3,{"id":148,"depth":437,"text":149},{"id":197,"depth":437,"text":198},{"id":228,"depth":437,"text":229},{"id":238,"depth":437,"text":238},{"id":247,"depth":437,"text":248},{"id":270,"depth":437,"text":270},{"id":330,"depth":437,"text":330},{"id":346,"depth":432,"text":347},[447,448],"学习笔记","TypeScript","2023-01-12",false,"md",null,{"comments":450,"slots":454},{},true,"\u002F2023\u002Ftypescript\u002Fts-4",{"text":458,"minutes":459,"time":460,"words":461},"7 min read",6.41,384600,1282,{"title":5,"description":15},{"loc":456,"images":464},[465],{"loc":33},"posts\u002F2023\u002Ftypescript\u002Fts-4",[448],"tech","92ghnpjNu8CzxdAZHsmITotoO28elNmc1nWLWGdn6OM",[471,476],{"title":472,"path":473,"stem":474,"date":475,"type":468,"children":-1},"TypeScript 中的类型（下）","\u002F2022\u002Ftypescript\u002Fts-3","posts\u002F2022\u002Ftypescript\u002Fts-3","2022-12-29",{"title":477,"path":478,"stem":479,"date":480,"type":468,"children":-1},"TS 中的类型运算：交叉类型","\u002F2023\u002Ftypescript\u002Fts-5","posts\u002F2023\u002Ftypescript\u002Fts-5","2023-01-17",1793699839549]