[{"data":1,"prerenderedAt":2651},["ShallowReactive",2],{"\u002Fwriting\u002Ftype-gymnastics-2":3},{"id":4,"title":5,"body":6,"date":2641,"description":50,"extension":2642,"intro":2643,"meta":2644,"navigation":502,"path":2645,"seo":2646,"stem":2647,"tags":2648,"__hash__":2650},"writing\u002Fwriting\u002Ftype-gymnastics-2.md","TS Gymnastics 2 - Build Basic Types",{"type":7,"value":8,"toc":2616},"minimark",[9,22,27,30,38,44,116,122,146,190,206,212,232,287,302,393,402,426,564,571,701,707,732,742,816,819,822,828,844,891,894,905,908,941,944,992,1003,1049,1052,1070,1104,1108,1118,1210,1216,1228,1273,1282,1291,1356,1365,1376,1460,1466,1481,1580,1584,1594,1726,1749,1847,1873,1876,1908,1927,1936,1942,1957,2125,2128,2140,2149,2263,2281,2285,2294,2382,2388,2424,2612],[10,11,12],"callout",{},[13,14,15,16,21],"p",{},"Previously on ",[17,18,20],"a",{"href":19},"type-gymnastics-1","TS Gymnastics",".",[23,24,26],"h2",{"id":25},"built-in-types","Built-in Types",[13,28,29],{},"利用基础的 TS 类型操作，可以创造某些常用的类型。这些类型也是 TS 类型系统内置的类型。",[31,32,34],"h3",{"id":33},"readonly",[35,36,37],"code",{},"Readonly",[13,39,40,41,43],{},"实现起来很简单，利用映射类型将",[35,42,33],{},"添加到所有属性上即可。",[45,46,51],"pre",{"className":47,"code":48,"language":49,"meta":50,"style":50},"language-ts shiki shiki-themes houston","type MyReadonly\u003CT> = {\n  readonly [P in keyof T]: T[P]\n}\n","ts","",[35,52,53,76,110],{"__ignoreMap":50},[54,55,58,62,66,70,73],"span",{"class":56,"line":57},"line",1,[54,59,61],{"class":60},"sqF6k","type",[54,63,65],{"class":64},"s8HWQ"," MyReadonly",[54,67,69],{"class":68},"sLo_3","\u003C",[54,71,72],{"class":64},"T",[54,74,75],{"class":68},"> = {\n",[54,77,79,82,85,88,91,94,97,100,102,105,107],{"class":56,"line":78},2,[54,80,81],{"class":60},"  readonly",[54,83,84],{"class":68}," [",[54,86,87],{"class":64},"P",[54,89,90],{"class":60}," in",[54,92,93],{"class":60}," keyof",[54,95,96],{"class":64}," T",[54,98,99],{"class":68},"]: ",[54,101,72],{"class":64},[54,103,104],{"class":68},"[",[54,106,87],{"class":64},[54,108,109],{"class":68},"]\n",[54,111,113],{"class":56,"line":112},3,[54,114,115],{"class":68},"}\n",[31,117,119],{"id":118},"exclude",[35,120,121],{},"Exclude",[13,123,124,127,128,130,131,134,135,138,139,142,143,145],{},[35,125,126],{},"Exclude\u003CT, U>","用于从联合类型中",[35,129,72],{},"排除指定类型",[35,132,133],{},"U","。从联合类型中排除类型可以通过在",[35,136,137],{},"extends","子句中返回",[35,140,141],{},"never","实现，问题的关键是构建",[35,144,137],{},"语句。",[45,147,149],{"className":47,"code":148,"language":49,"meta":50,"style":50},"type MyExclude\u003CT, U> = T extends U ? never : T\n",[35,150,151],{"__ignoreMap":50},[54,152,153,155,158,160,162,165,167,170,172,175,178,181,184,187],{"class":56,"line":57},[54,154,61],{"class":60},[54,156,157],{"class":64}," MyExclude",[54,159,69],{"class":68},[54,161,72],{"class":64},[54,163,164],{"class":68},", ",[54,166,133],{"class":64},[54,168,169],{"class":68},"> = ",[54,171,72],{"class":64},[54,173,174],{"class":60}," extends",[54,176,177],{"class":64}," U",[54,179,180],{"class":60}," ?",[54,182,183],{"class":64}," never",[54,185,186],{"class":60}," :",[54,188,189],{"class":64}," T\n",[13,191,192,193,195,196,200,201,205],{},"此处利用了",[35,194,137],{},"的",[197,198,199],"strong",{},"分发性质","，在",[17,202,204],{"href":203},"type-gymnastics-1#%E6%9D%A1%E4%BB%B6%E7%B1%BB%E5%9E%8B","上一篇","中已有介绍。",[31,207,209],{"id":208},"awaited",[35,210,211],{},"Awaited",[13,213,214,217,218,221,222,224,225,227,228,231],{},[35,215,216],{},"Awaited\u003CT>","用于从",[35,219,220],{},"Promise\u003CT>","中提取类型",[35,223,72],{},"，而提取类型正是",[35,226,137],{},"和",[35,229,230],{},"infer","组合的常见适用场景。",[45,233,235],{"className":47,"code":234,"language":49,"meta":50,"style":50},"type MyAwaited\u003CT extends Promise\u003Cany>> = T extends Promise\u003Cinfer R> ? R : never\n",[35,236,237],{"__ignoreMap":50},[54,238,239,241,244,246,248,250,253,255,258,261,263,265,267,270,273,276,279,282,284],{"class":56,"line":57},[54,240,61],{"class":60},[54,242,243],{"class":64}," MyAwaited",[54,245,69],{"class":68},[54,247,72],{"class":64},[54,249,174],{"class":60},[54,251,252],{"class":64}," Promise",[54,254,69],{"class":68},[54,256,257],{"class":64},"any",[54,259,260],{"class":68},">> = ",[54,262,72],{"class":64},[54,264,174],{"class":60},[54,266,252],{"class":64},[54,268,269],{"class":68},"\u003Cinfer ",[54,271,272],{"class":64},"R",[54,274,275],{"class":68},"> ",[54,277,278],{"class":60},"?",[54,280,281],{"class":64}," R",[54,283,186],{"class":60},[54,285,286],{"class":64}," never\n",[13,288,289,290,293,294,297,298,301],{},"如果考虑到",[35,291,292],{},"infer R","可能是",[35,295,296],{},"Promise","类型，需要递归调用",[35,299,300],{},"MyAwaited","。",[45,303,305],{"className":47,"code":304,"language":49,"meta":50,"style":50},"type MyAwaited\u003CT extends Promise\u003Cany>> =\n  T extends PromiseLike\u003Cinfer R>\n    ? R extends Promise\u003Cany>\n      ? MyAwaited\u003CR>\n      : R\n    : never\n",[35,306,307,328,345,362,376,385],{"__ignoreMap":50},[54,308,309,311,313,315,317,319,321,323,325],{"class":56,"line":57},[54,310,61],{"class":60},[54,312,243],{"class":64},[54,314,69],{"class":68},[54,316,72],{"class":64},[54,318,174],{"class":60},[54,320,252],{"class":64},[54,322,69],{"class":68},[54,324,257],{"class":64},[54,326,327],{"class":68},">> =\n",[54,329,330,333,335,338,340,342],{"class":56,"line":78},[54,331,332],{"class":64},"  T",[54,334,174],{"class":60},[54,336,337],{"class":64}," PromiseLike",[54,339,269],{"class":68},[54,341,272],{"class":64},[54,343,344],{"class":68},">\n",[54,346,347,350,352,354,356,358,360],{"class":56,"line":112},[54,348,349],{"class":60},"    ?",[54,351,281],{"class":64},[54,353,174],{"class":60},[54,355,252],{"class":64},[54,357,69],{"class":68},[54,359,257],{"class":64},[54,361,344],{"class":68},[54,363,365,368,370,372,374],{"class":56,"line":364},4,[54,366,367],{"class":60},"      ?",[54,369,243],{"class":64},[54,371,69],{"class":68},[54,373,272],{"class":64},[54,375,344],{"class":68},[54,377,379,382],{"class":56,"line":378},5,[54,380,381],{"class":60},"      :",[54,383,384],{"class":64}," R\n",[54,386,388,391],{"class":56,"line":387},6,[54,389,390],{"class":60},"    :",[54,392,286],{"class":64},[31,394,396,227,399],{"id":395},"parameters和returntype",[35,397,398],{},"Parameters",[35,400,401],{},"ReturnType",[13,403,404,407,408,410,411,414,415,417,418,227,420,422,423,425],{},[35,405,406],{},"Parameters\u003CT>","用于从函数类型",[35,409,72],{},"中提取参数的类型，而",[35,412,413],{},"ReturnType\u003CT>","用于提取函数",[35,416,72],{},"的返回值的类型。这两个类型都是从函数中提取类型，依然同上面的问题一样需要使用",[35,419,137],{},[35,421,230],{},"的组合。事实上，两者的唯一区别就在于",[35,424,230],{},"推断的位置不同。",[45,427,429],{"className":47,"code":428,"language":49,"meta":50,"style":50},"type MyParameters\u003CT extends (...args: any[]) => any> =\n  T extends (...args: infer R) => any ? R : never\n\ntype MyReturnType\u003CT extends (...args: any[]) => any> =\n  T extends (...args: any[]) => infer R ? R : never\n",[35,430,431,468,498,504,533],{"__ignoreMap":50},[54,432,433,435,438,440,442,444,447,451,454,456,459,462,465],{"class":56,"line":57},[54,434,61],{"class":60},[54,436,437],{"class":64}," MyParameters",[54,439,69],{"class":68},[54,441,72],{"class":64},[54,443,174],{"class":60},[54,445,446],{"class":68}," (...",[54,448,450],{"class":449},"smONe","args",[54,452,453],{"class":68},": ",[54,455,257],{"class":64},[54,457,458],{"class":68},"[]) ",[54,460,461],{"class":60},"=>",[54,463,464],{"class":64}," any",[54,466,467],{"class":68},"> =\n",[54,469,470,472,474,476,478,481,483,486,488,490,492,494,496],{"class":56,"line":78},[54,471,332],{"class":64},[54,473,174],{"class":60},[54,475,446],{"class":68},[54,477,450],{"class":449},[54,479,480],{"class":68},": infer ",[54,482,272],{"class":64},[54,484,485],{"class":68},") ",[54,487,461],{"class":60},[54,489,464],{"class":64},[54,491,180],{"class":60},[54,493,281],{"class":64},[54,495,186],{"class":60},[54,497,286],{"class":64},[54,499,500],{"class":56,"line":112},[54,501,503],{"emptyLinePlaceholder":502},true,"\n",[54,505,506,508,511,513,515,517,519,521,523,525,527,529,531],{"class":56,"line":364},[54,507,61],{"class":60},[54,509,510],{"class":64}," MyReturnType",[54,512,69],{"class":68},[54,514,72],{"class":64},[54,516,174],{"class":60},[54,518,446],{"class":68},[54,520,450],{"class":449},[54,522,453],{"class":68},[54,524,257],{"class":64},[54,526,458],{"class":68},[54,528,461],{"class":60},[54,530,464],{"class":64},[54,532,467],{"class":68},[54,534,535,537,539,541,543,545,547,549,551,554,556,558,560,562],{"class":56,"line":378},[54,536,332],{"class":64},[54,538,174],{"class":60},[54,540,446],{"class":68},[54,542,450],{"class":449},[54,544,453],{"class":68},[54,546,257],{"class":64},[54,548,458],{"class":68},[54,550,461],{"class":60},[54,552,553],{"class":68}," infer ",[54,555,272],{"class":64},[54,557,180],{"class":60},[54,559,281],{"class":64},[54,561,186],{"class":60},[54,563,286],{"class":64},[13,565,566,567,570],{},"值得一提的是，类型推断的结果与",[197,568,569],{},"泛型类型签名","有关。观察下面的例子：",[45,572,574],{"className":47,"code":573,"language":49,"meta":50,"style":50},"const fn1 = (v: boolean) => v ? 1 : 2\nconst fn2 = (v: boolean): number => v ? 1 : 2\n\ntype P1 = MyReturnType\u003Ctypeof fn1> \u002F\u002F Type: 1 | 2\ntype P2 = MyReturnType\u003Ctypeof fn2> \u002F\u002F Type: number\n",[35,575,576,615,649,653,679],{"__ignoreMap":50},[54,577,578,581,585,588,591,593,596,598,600,604,606,610,612],{"class":56,"line":57},[54,579,580],{"class":60},"const",[54,582,584],{"class":583},"stHQG"," fn1",[54,586,587],{"class":68}," = (",[54,589,590],{"class":449},"v",[54,592,453],{"class":68},[54,594,595],{"class":64},"boolean",[54,597,485],{"class":68},[54,599,461],{"class":60},[54,601,603],{"class":602},"sb16X"," v",[54,605,180],{"class":60},[54,607,609],{"class":608},"se2J0"," 1",[54,611,186],{"class":60},[54,613,614],{"class":608}," 2\n",[54,616,617,619,622,624,626,628,630,633,636,639,641,643,645,647],{"class":56,"line":78},[54,618,580],{"class":60},[54,620,621],{"class":583}," fn2",[54,623,587],{"class":68},[54,625,590],{"class":449},[54,627,453],{"class":68},[54,629,595],{"class":64},[54,631,632],{"class":68},"): ",[54,634,635],{"class":64},"number",[54,637,638],{"class":60}," =>",[54,640,603],{"class":602},[54,642,180],{"class":60},[54,644,609],{"class":608},[54,646,186],{"class":60},[54,648,614],{"class":608},[54,650,651],{"class":56,"line":112},[54,652,503],{"emptyLinePlaceholder":502},[54,654,655,657,660,663,666,668,671,673,675],{"class":56,"line":364},[54,656,61],{"class":60},[54,658,659],{"class":64}," P1",[54,661,662],{"class":68}," = ",[54,664,665],{"class":64},"MyReturnType",[54,667,69],{"class":68},[54,669,670],{"class":60},"typeof",[54,672,584],{"class":602},[54,674,275],{"class":68},[54,676,678],{"class":677},"sy89t","\u002F\u002F Type: 1 | 2\n",[54,680,681,683,686,688,690,692,694,696,698],{"class":56,"line":378},[54,682,61],{"class":60},[54,684,685],{"class":64}," P2",[54,687,662],{"class":68},[54,689,665],{"class":64},[54,691,69],{"class":68},[54,693,670],{"class":60},[54,695,621],{"class":602},[54,697,275],{"class":68},[54,699,700],{"class":677},"\u002F\u002F Type: number\n",[31,702,704],{"id":703},"omit",[35,705,706],{},"Omit",[13,708,709,712,713,715,716,719,720,722,723,725,726,728,729,731],{},[35,710,711],{},"Omit\u003CT, K>","用于从类型",[35,714,72],{},"中排除指定属性",[35,717,718],{},"K","，与",[35,721,121],{},"的区别在于",[35,724,706],{},"是排除",[35,727,72],{},"中的属性（键），而",[35,730,121],{},"是排除联合类型中的类型。",[13,733,734,735,738,739,741],{},"在",[17,736,204],{"href":737},"type-gymnastics-1#%E6%98%A0%E5%B0%84%E7%B1%BB%E5%9E%8B","中已经介绍过，通过在键上使用",[35,740,141],{},"可以排除该属性。",[45,743,745],{"className":47,"code":744,"language":49,"meta":50,"style":50},"type MyOmit\u003CT, K extends keyof T> = {\n  [P in keyof T as P extends K ? never : P]: T[P]\n}\n",[35,746,747,770,812],{"__ignoreMap":50},[54,748,749,751,754,756,758,760,762,764,766,768],{"class":56,"line":57},[54,750,61],{"class":60},[54,752,753],{"class":64}," MyOmit",[54,755,69],{"class":68},[54,757,72],{"class":64},[54,759,164],{"class":68},[54,761,718],{"class":64},[54,763,174],{"class":60},[54,765,93],{"class":60},[54,767,96],{"class":64},[54,769,75],{"class":68},[54,771,772,775,777,779,781,783,786,789,791,794,796,798,800,802,804,806,808,810],{"class":56,"line":78},[54,773,774],{"class":68},"  [",[54,776,87],{"class":64},[54,778,90],{"class":60},[54,780,93],{"class":60},[54,782,96],{"class":64},[54,784,785],{"class":60}," as",[54,787,788],{"class":64}," P",[54,790,174],{"class":60},[54,792,793],{"class":64}," K",[54,795,180],{"class":60},[54,797,183],{"class":64},[54,799,186],{"class":60},[54,801,788],{"class":64},[54,803,99],{"class":68},[54,805,72],{"class":64},[54,807,104],{"class":68},[54,809,87],{"class":64},[54,811,109],{"class":68},[54,813,814],{"class":56,"line":112},[54,815,115],{"class":68},[23,817,818],{"id":818},"常用类型",[13,820,821],{},"在复杂的类型体操（本系列不会涉及）中，往往需要封装一组常用的基本类型来实现某些算法。",[31,823,825],{"id":824},"if",[35,826,827],{},"If",[13,829,830,833,834,837,838,840,841,301],{},[35,831,832],{},"If\u003CC, T, F>","用于根据条件",[35,835,836],{},"C","判断是否返回类型",[35,839,72],{},"，否则返回类型",[35,842,843],{},"F",[45,845,847],{"className":47,"code":846,"language":49,"meta":50,"style":50},"type If\u003CC extends boolean, T, F> = C extends true ? T : F\n",[35,848,849],{"__ignoreMap":50},[54,850,851,853,856,858,860,862,865,867,869,871,873,875,877,879,882,884,886,888],{"class":56,"line":57},[54,852,61],{"class":60},[54,854,855],{"class":64}," If",[54,857,69],{"class":68},[54,859,836],{"class":64},[54,861,174],{"class":60},[54,863,864],{"class":64}," boolean",[54,866,164],{"class":68},[54,868,72],{"class":64},[54,870,164],{"class":68},[54,872,843],{"class":64},[54,874,169],{"class":68},[54,876,836],{"class":64},[54,878,174],{"class":60},[54,880,881],{"class":64}," true",[54,883,180],{"class":60},[54,885,96],{"class":64},[54,887,186],{"class":60},[54,889,890],{"class":64}," F\n",[31,892,893],{"id":893},"元组转对象",[13,895,896,897,900,901,904],{},"将给定的元组（例如",[35,898,899],{},"['a', 'b', 'c']","）转换为对象（",[35,902,903],{},"{ a: 'a', b: 'b', c: 'c' }","）。",[13,906,907],{},"首先，需要确定输入的泛型元素为元组，且需要能够作为对象的键。",[45,909,911],{"className":47,"code":910,"language":49,"meta":50,"style":50},"type TupleToObject\u003CT extends readonly PropertyKey[]> = { \u002F* *\u002F }\n",[35,912,913],{"__ignoreMap":50},[54,914,915,917,920,922,924,926,929,932,935,938],{"class":56,"line":57},[54,916,61],{"class":60},[54,918,919],{"class":64}," TupleToObject",[54,921,69],{"class":68},[54,923,72],{"class":64},[54,925,174],{"class":60},[54,927,928],{"class":60}," readonly",[54,930,931],{"class":64}," PropertyKey",[54,933,934],{"class":68},"[]> = { ",[54,936,937],{"class":677},"\u002F* *\u002F",[54,939,940],{"class":68}," }\n",[13,942,943],{},"接下来，需要构造映射类型：",[45,945,947],{"className":47,"code":946,"language":49,"meta":50,"style":50},"type TupleToObject\u003CT extends readonly PropertyKey[]> = {\n  [K in \u003CT中的每个值>]: K\n}\n",[35,948,949,968,988],{"__ignoreMap":50},[54,950,951,953,955,957,959,961,963,965],{"class":56,"line":57},[54,952,61],{"class":60},[54,954,919],{"class":64},[54,956,69],{"class":68},[54,958,72],{"class":64},[54,960,174],{"class":60},[54,962,928],{"class":60},[54,964,931],{"class":64},[54,966,967],{"class":68},"[]> = {\n",[54,969,970,972,974,976,979,982,985],{"class":56,"line":78},[54,971,774],{"class":68},[54,973,718],{"class":64},[54,975,90],{"class":60},[54,977,978],{"class":68}," \u003C",[54,980,981],{"class":64},"T中的每个值",[54,983,984],{"class":68},">]: ",[54,986,987],{"class":64},"K\n",[54,989,990],{"class":56,"line":112},[54,991,115],{"class":68},[13,993,994,995,998,999,1002],{},"最后的问题在于如何获取元组中的每个值。在",[17,996,204],{"href":997},"type-gymnastics-1#%E5%85%83%E7%BB%84%E7%B1%BB%E5%9E%8B","中提及，只需要使用",[35,1000,1001],{},"[number]","索引访问即可。",[45,1004,1006],{"className":47,"code":1005,"language":49,"meta":50,"style":50},"type TupleToObject\u003CT extends readonly PropertyKey[]> = {\n  [K in T[number]]: K\n}\n",[35,1007,1008,1026,1045],{"__ignoreMap":50},[54,1009,1010,1012,1014,1016,1018,1020,1022,1024],{"class":56,"line":57},[54,1011,61],{"class":60},[54,1013,919],{"class":64},[54,1015,69],{"class":68},[54,1017,72],{"class":64},[54,1019,174],{"class":60},[54,1021,928],{"class":60},[54,1023,931],{"class":64},[54,1025,967],{"class":68},[54,1027,1028,1030,1032,1034,1036,1038,1040,1043],{"class":56,"line":78},[54,1029,774],{"class":68},[54,1031,718],{"class":64},[54,1033,90],{"class":60},[54,1035,96],{"class":64},[54,1037,104],{"class":68},[54,1039,635],{"class":64},[54,1041,1042],{"class":68},"]]: ",[54,1044,987],{"class":64},[54,1046,1047],{"class":56,"line":112},[54,1048,115],{"class":68},[31,1050,1051],{"id":1051},"获取元组的长度",[13,1053,1054,1055,1059,1060,1062,1063,1066,1067,1069],{},"在前一篇介绍",[17,1056,1058],{"href":1057},"type-gymnastics-1#keyof%E7%B1%BB%E5%9E%8B%E7%B3%BB%E7%BB%9F%E7%9A%84%E5%8F%8D%E5%B0%84","keyof","中提及，如果把数组看成是一个类型，那么",[35,1061,1058],{},"可以获取到数组的属性，其中也包括",[35,1064,1065],{},"length","。只要读取",[35,1068,1065],{},"属性，就能获取元组的长度。",[45,1071,1073],{"className":47,"code":1072,"language":49,"meta":50,"style":50},"type Length\u003CT extends readonly any[]> = T['length']\n",[35,1074,1075],{"__ignoreMap":50},[54,1076,1077,1079,1082,1084,1086,1088,1090,1092,1095,1097,1099,1102],{"class":56,"line":57},[54,1078,61],{"class":60},[54,1080,1081],{"class":64}," Length",[54,1083,69],{"class":68},[54,1085,72],{"class":64},[54,1087,174],{"class":60},[54,1089,928],{"class":60},[54,1091,464],{"class":64},[54,1093,1094],{"class":68},"[]> = ",[54,1096,72],{"class":64},[54,1098,104],{"class":68},[54,1100,1101],{"class":608},"'length'",[54,1103,109],{"class":68},[31,1105,1107],{"id":1106},"获取数组的第一个最后一个元素","获取数组的第一个\u002F最后一个元素",[13,1109,1110,1111,1114,1115,1117],{},"这两个工具类型的实现较为相似。这也涉及到",[197,1112,1113],{},"从某个类型中提取一个类型","的操作，因此使用",[35,1116,230],{},"进行推断是自然而然的。",[45,1119,1121],{"className":47,"code":1120,"language":49,"meta":50,"style":50},"type First\u003CT extends any[]> = T extends [infer F, ...infer _] ? F : never\ntype Last\u003CT extends any[]> = T extends [...infer _, infer L] ? L : never\n",[35,1122,1123,1167],{"__ignoreMap":50},[54,1124,1125,1127,1130,1132,1134,1136,1138,1140,1142,1144,1147,1149,1152,1155,1158,1160,1163,1165],{"class":56,"line":57},[54,1126,61],{"class":60},[54,1128,1129],{"class":64}," First",[54,1131,69],{"class":68},[54,1133,72],{"class":64},[54,1135,174],{"class":60},[54,1137,464],{"class":64},[54,1139,1094],{"class":68},[54,1141,72],{"class":64},[54,1143,174],{"class":60},[54,1145,1146],{"class":68}," [infer ",[54,1148,843],{"class":64},[54,1150,1151],{"class":68},", ...infer ",[54,1153,1154],{"class":64},"_",[54,1156,1157],{"class":68},"] ",[54,1159,278],{"class":60},[54,1161,1162],{"class":64}," F",[54,1164,186],{"class":60},[54,1166,286],{"class":64},[54,1168,1169,1171,1174,1176,1178,1180,1182,1184,1186,1188,1191,1193,1196,1199,1201,1203,1206,1208],{"class":56,"line":78},[54,1170,61],{"class":60},[54,1172,1173],{"class":64}," Last",[54,1175,69],{"class":68},[54,1177,72],{"class":64},[54,1179,174],{"class":60},[54,1181,464],{"class":64},[54,1183,1094],{"class":68},[54,1185,72],{"class":64},[54,1187,174],{"class":60},[54,1189,1190],{"class":68}," [...infer ",[54,1192,1154],{"class":64},[54,1194,1195],{"class":68},", infer ",[54,1197,1198],{"class":64},"L",[54,1200,1157],{"class":68},[54,1202,278],{"class":60},[54,1204,1205],{"class":64}," L",[54,1207,186],{"class":60},[54,1209,286],{"class":64},[31,1211,1213],{"id":1212},"concat",[35,1214,1215],{},"Concat",[13,1217,1218,1219,1222,1223,227,1225,1227],{},"实现",[35,1220,1221],{},"Concat\u003CT, U>","类型，用于将两个元组",[35,1224,72],{},[35,1226,133],{},"拼接起来。利用扩展运算符即可实现拼接。",[45,1229,1231],{"className":47,"code":1230,"language":49,"meta":50,"style":50},"type Concat\u003CT extends readonly any[], U extends readonly any[]> = [...T, ...U]\n",[35,1232,1233],{"__ignoreMap":50},[54,1234,1235,1237,1240,1242,1244,1246,1248,1250,1253,1255,1257,1259,1261,1264,1266,1269,1271],{"class":56,"line":57},[54,1236,61],{"class":60},[54,1238,1239],{"class":64}," Concat",[54,1241,69],{"class":68},[54,1243,72],{"class":64},[54,1245,174],{"class":60},[54,1247,928],{"class":60},[54,1249,464],{"class":64},[54,1251,1252],{"class":68},"[], ",[54,1254,133],{"class":64},[54,1256,174],{"class":60},[54,1258,928],{"class":60},[54,1260,464],{"class":64},[54,1262,1263],{"class":68},"[]> = [...",[54,1265,72],{"class":64},[54,1267,1268],{"class":68},", ...",[54,1270,133],{"class":64},[54,1272,109],{"class":68},[31,1274,1276,227,1279],{"id":1275},"push和unshift",[35,1277,1278],{},"Push",[35,1280,1281],{},"Unshift",[13,1283,1284,1285,1287,1288,1290],{},"与",[35,1286,1215],{},"类似，这两个类型都是在数组中增加元素。唯一的区别就是",[35,1289,133],{},"不是数组，但依然使用扩展运算符实现。",[45,1292,1294],{"className":47,"code":1293,"language":49,"meta":50,"style":50},"type Push\u003CT extends any[], U> = [...T, U]\ntype Unshift\u003CT extends any[], U> = [U, ...T]\n",[35,1295,1296,1326],{"__ignoreMap":50},[54,1297,1298,1300,1303,1305,1307,1309,1311,1313,1315,1318,1320,1322,1324],{"class":56,"line":57},[54,1299,61],{"class":60},[54,1301,1302],{"class":64}," Push",[54,1304,69],{"class":68},[54,1306,72],{"class":64},[54,1308,174],{"class":60},[54,1310,464],{"class":64},[54,1312,1252],{"class":68},[54,1314,133],{"class":64},[54,1316,1317],{"class":68},"> = [...",[54,1319,72],{"class":64},[54,1321,164],{"class":68},[54,1323,133],{"class":64},[54,1325,109],{"class":68},[54,1327,1328,1330,1333,1335,1337,1339,1341,1343,1345,1348,1350,1352,1354],{"class":56,"line":78},[54,1329,61],{"class":60},[54,1331,1332],{"class":64}," Unshift",[54,1334,69],{"class":68},[54,1336,72],{"class":64},[54,1338,174],{"class":60},[54,1340,464],{"class":64},[54,1342,1252],{"class":68},[54,1344,133],{"class":64},[54,1346,1347],{"class":68},"> = [",[54,1349,133],{"class":64},[54,1351,1268],{"class":68},[54,1353,72],{"class":64},[54,1355,109],{"class":68},[31,1357,1359,227,1362],{"id":1358},"pop和shift",[35,1360,1361],{},"Pop",[35,1363,1364],{},"Shift",[13,1366,1367,1368,1372,1373,1375],{},"这两个类型都是从数组中移除第一个或最后一个元素，需要先",[17,1369,1371],{"href":1370},"#%E8%8E%B7%E5%8F%96%E6%95%B0%E7%BB%84%E7%9A%84%E7%AC%AC%E4%B8%80%E4%B8%AA%E6%9C%80%E5%90%8E%E4%B8%80%E4%B8%AA%E5%85%83%E7%B4%A0","#获取数组的第一个\u002F最后一个元素","，然后返回",[35,1374,230],{},"推断的剩余部分。",[45,1377,1379],{"className":47,"code":1378,"language":49,"meta":50,"style":50},"type Pop\u003CT extends any[]> = T extends [...infer R, infer _] ? R : []\ntype Shift\u003CT extends any[]> = T extends [infer _, ...infer R] ? R : []\n",[35,1380,1381,1421],{"__ignoreMap":50},[54,1382,1383,1385,1388,1390,1392,1394,1396,1398,1400,1402,1404,1406,1408,1410,1412,1414,1416,1418],{"class":56,"line":57},[54,1384,61],{"class":60},[54,1386,1387],{"class":64}," Pop",[54,1389,69],{"class":68},[54,1391,72],{"class":64},[54,1393,174],{"class":60},[54,1395,464],{"class":64},[54,1397,1094],{"class":68},[54,1399,72],{"class":64},[54,1401,174],{"class":60},[54,1403,1190],{"class":68},[54,1405,272],{"class":64},[54,1407,1195],{"class":68},[54,1409,1154],{"class":64},[54,1411,1157],{"class":68},[54,1413,278],{"class":60},[54,1415,281],{"class":64},[54,1417,186],{"class":60},[54,1419,1420],{"class":68}," []\n",[54,1422,1423,1425,1428,1430,1432,1434,1436,1438,1440,1442,1444,1446,1448,1450,1452,1454,1456,1458],{"class":56,"line":78},[54,1424,61],{"class":60},[54,1426,1427],{"class":64}," Shift",[54,1429,69],{"class":68},[54,1431,72],{"class":64},[54,1433,174],{"class":60},[54,1435,464],{"class":64},[54,1437,1094],{"class":68},[54,1439,72],{"class":64},[54,1441,174],{"class":60},[54,1443,1146],{"class":68},[54,1445,1154],{"class":64},[54,1447,1151],{"class":68},[54,1449,272],{"class":64},[54,1451,1157],{"class":68},[54,1453,278],{"class":60},[54,1455,281],{"class":64},[54,1457,186],{"class":60},[54,1459,1420],{"class":68},[31,1461,1463],{"id":1462},"equal",[35,1464,1465],{},"Equal",[13,1467,1468,1471,1472,227,1474,1476,1477,1480],{},[35,1469,1470],{},"Equal\u003CT, U>","用于判断类型",[35,1473,72],{},[35,1475,133],{},"是否严格相等。这是一个非常经典的反直觉案例。它的核心是利用",[197,1478,1479],{},"函数参数的逆变","和条件类型构造的「严格类型等价判断」。在此直接给出实现：",[45,1482,1484],{"className":47,"code":1483,"language":49,"meta":50,"style":50},"type IsEqual\u003CT, U> =\n    (\u003CG>() => G extends T ? 1 : 2) extends\n    (\u003CG>() => G extends U ? 1 : 2)\n        ? true\n        : false\n",[35,1485,1486,1503,1537,1564,1572],{"__ignoreMap":50},[54,1487,1488,1490,1493,1495,1497,1499,1501],{"class":56,"line":57},[54,1489,61],{"class":60},[54,1491,1492],{"class":64}," IsEqual",[54,1494,69],{"class":68},[54,1496,72],{"class":64},[54,1498,164],{"class":68},[54,1500,133],{"class":64},[54,1502,467],{"class":68},[54,1504,1505,1508,1511,1514,1516,1519,1521,1523,1525,1527,1529,1532,1534],{"class":56,"line":78},[54,1506,1507],{"class":68},"    (\u003C",[54,1509,1510],{"class":64},"G",[54,1512,1513],{"class":68},">() ",[54,1515,461],{"class":60},[54,1517,1518],{"class":64}," G",[54,1520,174],{"class":60},[54,1522,96],{"class":64},[54,1524,180],{"class":60},[54,1526,609],{"class":608},[54,1528,186],{"class":60},[54,1530,1531],{"class":608}," 2",[54,1533,485],{"class":68},[54,1535,1536],{"class":60},"extends\n",[54,1538,1539,1541,1543,1545,1547,1549,1551,1553,1555,1557,1559,1561],{"class":56,"line":112},[54,1540,1507],{"class":68},[54,1542,1510],{"class":64},[54,1544,1513],{"class":68},[54,1546,461],{"class":60},[54,1548,1518],{"class":64},[54,1550,174],{"class":60},[54,1552,177],{"class":64},[54,1554,180],{"class":60},[54,1556,609],{"class":608},[54,1558,186],{"class":60},[54,1560,1531],{"class":608},[54,1562,1563],{"class":68},")\n",[54,1565,1566,1569],{"class":56,"line":364},[54,1567,1568],{"class":60},"        ?",[54,1570,1571],{"class":64}," true\n",[54,1573,1574,1577],{"class":56,"line":378},[54,1575,1576],{"class":60},"        :",[54,1578,1579],{"class":64}," false\n",[1581,1582,1583],"h4",{"id":1583},"错误实现",[13,1585,1586,1587,227,1590,1593],{},"那么，为什么不能直接使用",[35,1588,1589],{},"T extends U",[35,1591,1592],{},"U extends T","来判断类型是否相等呢？考虑以下情况：",[45,1595,1597],{"className":47,"code":1596,"language":49,"meta":50,"style":50},"type IncorrectEqual\u003CT, U> = T extends U\n  ? U extends T\n    ? true\n    : false\n  : false\n\n\u002F\u002F 含有 any\ntype A = IncorrectEqual\u003Cany, 1> \u002F\u002F boolean, expected false\n\u002F\u002F 含有联合类型\ntype B = IncorrectEqual\u003C1 | 2, 1> \u002F\u002F boolean, expected false\n",[35,1598,1599,1623,1634,1640,1646,1653,1657,1663,1690,1696],{"__ignoreMap":50},[54,1600,1601,1603,1606,1608,1610,1612,1614,1616,1618,1620],{"class":56,"line":57},[54,1602,61],{"class":60},[54,1604,1605],{"class":64}," IncorrectEqual",[54,1607,69],{"class":68},[54,1609,72],{"class":64},[54,1611,164],{"class":68},[54,1613,133],{"class":64},[54,1615,169],{"class":68},[54,1617,72],{"class":64},[54,1619,174],{"class":60},[54,1621,1622],{"class":64}," U\n",[54,1624,1625,1628,1630,1632],{"class":56,"line":78},[54,1626,1627],{"class":60},"  ?",[54,1629,177],{"class":64},[54,1631,174],{"class":60},[54,1633,189],{"class":64},[54,1635,1636,1638],{"class":56,"line":112},[54,1637,349],{"class":60},[54,1639,1571],{"class":64},[54,1641,1642,1644],{"class":56,"line":364},[54,1643,390],{"class":60},[54,1645,1579],{"class":64},[54,1647,1648,1651],{"class":56,"line":378},[54,1649,1650],{"class":60},"  :",[54,1652,1579],{"class":64},[54,1654,1655],{"class":56,"line":387},[54,1656,503],{"emptyLinePlaceholder":502},[54,1658,1660],{"class":56,"line":1659},7,[54,1661,1662],{"class":677},"\u002F\u002F 含有 any\n",[54,1664,1666,1668,1671,1673,1676,1678,1680,1682,1685,1687],{"class":56,"line":1665},8,[54,1667,61],{"class":60},[54,1669,1670],{"class":64}," A",[54,1672,662],{"class":68},[54,1674,1675],{"class":64},"IncorrectEqual",[54,1677,69],{"class":68},[54,1679,257],{"class":64},[54,1681,164],{"class":68},[54,1683,1684],{"class":608},"1",[54,1686,275],{"class":68},[54,1688,1689],{"class":677},"\u002F\u002F boolean, expected false\n",[54,1691,1693],{"class":56,"line":1692},9,[54,1694,1695],{"class":677},"\u002F\u002F 含有联合类型\n",[54,1697,1699,1701,1704,1706,1708,1710,1712,1715,1718,1720,1722,1724],{"class":56,"line":1698},10,[54,1700,61],{"class":60},[54,1702,1703],{"class":64}," B",[54,1705,662],{"class":68},[54,1707,1675],{"class":64},[54,1709,69],{"class":68},[54,1711,1684],{"class":608},[54,1713,1714],{"class":68}," | ",[54,1716,1717],{"class":608},"2",[54,1719,164],{"class":68},[54,1721,1684],{"class":608},[54,1723,275],{"class":68},[54,1725,1689],{"class":677},[13,1727,1728,1729,227,1732,1735,1736,1738,1739,1742,1743,1745,1746,1748],{},"在继续之前，需要分析一下为什么上面的例子中",[35,1730,1731],{},"A",[35,1733,1734],{},"B","会有这样的结果。首先看含有联合类型的",[35,1737,1734],{},"。当",[35,1740,1741],{},"T = 1 | 2","时，因为",[35,1744,72],{},"是裸类型参数，所以",[35,1747,1589],{},"会触发类型分发。带入分发可得到等价类型：",[45,1750,1752],{"className":47,"code":1751,"language":49,"meta":50,"style":50},"(1 extends 1 ? 1 extends 1 ? ...) | (2 extends 1 ? 1 extends 2 ? ...)\n\n\u002F\u002F 其中\n1 extends 1 \u002F\u002F true\n2 extends 1 \u002F\u002F false\n\n\u002F\u002F 合并结果\n\u002F\u002F true | false\n\u002F\u002F TS 自动合并为 boolean\n",[35,1753,1754,1797,1801,1806,1817,1828,1832,1837,1842],{"__ignoreMap":50},[54,1755,1756,1759,1761,1763,1765,1767,1769,1771,1773,1775,1778,1780,1782,1784,1786,1788,1790,1792,1794],{"class":56,"line":57},[54,1757,1758],{"class":68},"(",[54,1760,1684],{"class":608},[54,1762,174],{"class":602},[54,1764,609],{"class":608},[54,1766,180],{"class":60},[54,1768,609],{"class":608},[54,1770,174],{"class":602},[54,1772,609],{"class":608},[54,1774,180],{"class":60},[54,1776,1777],{"class":68}," ...) | (",[54,1779,1717],{"class":608},[54,1781,174],{"class":602},[54,1783,609],{"class":608},[54,1785,180],{"class":60},[54,1787,609],{"class":608},[54,1789,174],{"class":602},[54,1791,1531],{"class":608},[54,1793,180],{"class":60},[54,1795,1796],{"class":68}," ...)\n",[54,1798,1799],{"class":56,"line":78},[54,1800,503],{"emptyLinePlaceholder":502},[54,1802,1803],{"class":56,"line":112},[54,1804,1805],{"class":677},"\u002F\u002F 其中\n",[54,1807,1808,1810,1812,1814],{"class":56,"line":364},[54,1809,1684],{"class":608},[54,1811,174],{"class":602},[54,1813,609],{"class":608},[54,1815,1816],{"class":677}," \u002F\u002F true\n",[54,1818,1819,1821,1823,1825],{"class":56,"line":378},[54,1820,1717],{"class":608},[54,1822,174],{"class":602},[54,1824,609],{"class":608},[54,1826,1827],{"class":677}," \u002F\u002F false\n",[54,1829,1830],{"class":56,"line":387},[54,1831,503],{"emptyLinePlaceholder":502},[54,1833,1834],{"class":56,"line":1659},[54,1835,1836],{"class":677},"\u002F\u002F 合并结果\n",[54,1838,1839],{"class":56,"line":1665},[54,1840,1841],{"class":677},"\u002F\u002F true | false\n",[54,1843,1844],{"class":56,"line":1692},[54,1845,1846],{"class":677},"\u002F\u002F TS 自动合并为 boolean\n",[13,1848,1849,1850,1852,1853,1855,1856,1859,1860,1863,1864,1867,1868,1870,1871,301],{},"再看类型",[35,1851,1731],{},"。因为",[35,1854,257],{},"可能为任何类型，因此会“分裂”为两个分支分别进行判断。分支 1 中，",[35,1857,1858],{},"1 extends any","为",[35,1861,1862],{},"true","，分支 2 直接返回",[35,1865,1866],{},"false","。因此，同上进行合并后，",[35,1869,1731],{},"的类型为",[35,1872,595],{},[1581,1874,1875],{"id":1875},"正确实现和原因",[13,1877,1878,1879,1882,1883,1886,1887,1890,1891,1895,1896,1859,1898,1900,1901,1904,1905,301],{},"那为何使用函数进行比较就能得到正确的结果呢？",[35,1880,1881],{},"IsEqual","的核心是判断",[35,1884,1885],{},"\u003CG>() => G extends T ? 1 : 2","是否为",[35,1888,1889],{},"\u003CG>() => G extends U ? 1 : 2","的子类型。这是含有泛型参数的函数，且函数无参，按照",[17,1892,1894],{"href":1893},".\u002Ftype-gymnastics-1#%E9%80%86%E5%8F%98%E4%B8%8E%E5%8D%8F%E5%8F%98","逆变与协变","中的介绍，若要使",[35,1897,1885],{},[35,1899,1889],{},"的子类型，则需要",[197,1902,1903],{},"对于任意类型 G","，都有",[35,1906,1907],{},"(G extends T ? 1 : 2) extends (G extends U ? 1 : 2)",[13,1909,1910,1911,1914,1915,1918,1919,1922,1923,1926],{},"不妨进行倒推。T 和 U 要么相等，要么不相等。假设",[35,1912,1913],{},"T ≠ U","，那么必定存在某个 G，使得",[35,1916,1917],{},"G extends T ≠ G extends U","。可以轻松证明。假设",[35,1920,1921],{},"T = U","，那么对于任意 G 都应当有",[35,1924,1925],{},"G extends T = G extends U","。显然，这种情况下等式成立。",[13,1928,1929,1930,1932,1933,1935],{},"综上，判断两个类型是否相等的",[35,1931,1465],{},"的实现，是建立在泛型函数的全称量化特性之上的。普通的",[35,1934,1589],{},"是在判断 T 是否是 U 的子集，而泛型函数的实现版本则是在判断 T 和 U 对于所有可能得输入 G，行为是否一致。",[31,1937,1939],{"id":1938},"includes",[35,1940,1941],{},"Includes",[13,1943,1944,1945,1947,1948,1950,1951,1953,1954,1956],{},"有了",[35,1946,1881],{},"的实现，就可以在此基础上实现数组的",[35,1949,1941],{},"类型。需要用到递归类型，依次比较数组",[35,1952,72],{},"中的每个类型是否与",[35,1955,133],{},"相等。",[45,1958,1960],{"className":47,"code":1959,"language":49,"meta":50,"style":50},"type IsEqual\u003CT, U> = (\u003CG>() => G extends T ? 1 : 2) extends (\u003CG>() => G extends U ? 1 : 2) ? true : false\n\ntype Includes\u003CT extends readonly any[], U> = T extends [infer F, ...infer R]\n  ? IsEqual\u003CF, U> extends true\n    ? true\n    : Includes\u003CR, U>\n  : false\n",[35,1961,1962,2036,2040,2077,2097,2103,2119],{"__ignoreMap":50},[54,1963,1964,1966,1968,1970,1972,1974,1976,1979,1981,1983,1985,1987,1989,1991,1993,1995,1997,1999,2001,2003,2006,2008,2010,2012,2014,2016,2018,2020,2022,2024,2026,2028,2030,2032,2034],{"class":56,"line":57},[54,1965,61],{"class":60},[54,1967,1492],{"class":64},[54,1969,69],{"class":68},[54,1971,72],{"class":64},[54,1973,164],{"class":68},[54,1975,133],{"class":64},[54,1977,1978],{"class":68},"> = (\u003C",[54,1980,1510],{"class":64},[54,1982,1513],{"class":68},[54,1984,461],{"class":60},[54,1986,1518],{"class":64},[54,1988,174],{"class":60},[54,1990,96],{"class":64},[54,1992,180],{"class":60},[54,1994,609],{"class":608},[54,1996,186],{"class":60},[54,1998,1531],{"class":608},[54,2000,485],{"class":68},[54,2002,137],{"class":60},[54,2004,2005],{"class":68}," (\u003C",[54,2007,1510],{"class":64},[54,2009,1513],{"class":68},[54,2011,461],{"class":60},[54,2013,1518],{"class":64},[54,2015,174],{"class":60},[54,2017,177],{"class":64},[54,2019,180],{"class":60},[54,2021,609],{"class":608},[54,2023,186],{"class":60},[54,2025,1531],{"class":608},[54,2027,485],{"class":68},[54,2029,278],{"class":60},[54,2031,881],{"class":64},[54,2033,186],{"class":60},[54,2035,1579],{"class":64},[54,2037,2038],{"class":56,"line":78},[54,2039,503],{"emptyLinePlaceholder":502},[54,2041,2042,2044,2047,2049,2051,2053,2055,2057,2059,2061,2063,2065,2067,2069,2071,2073,2075],{"class":56,"line":112},[54,2043,61],{"class":60},[54,2045,2046],{"class":64}," Includes",[54,2048,69],{"class":68},[54,2050,72],{"class":64},[54,2052,174],{"class":60},[54,2054,928],{"class":60},[54,2056,464],{"class":64},[54,2058,1252],{"class":68},[54,2060,133],{"class":64},[54,2062,169],{"class":68},[54,2064,72],{"class":64},[54,2066,174],{"class":60},[54,2068,1146],{"class":68},[54,2070,843],{"class":64},[54,2072,1151],{"class":68},[54,2074,272],{"class":64},[54,2076,109],{"class":68},[54,2078,2079,2081,2083,2085,2087,2089,2091,2093,2095],{"class":56,"line":364},[54,2080,1627],{"class":60},[54,2082,1492],{"class":64},[54,2084,69],{"class":68},[54,2086,843],{"class":64},[54,2088,164],{"class":68},[54,2090,133],{"class":64},[54,2092,275],{"class":68},[54,2094,137],{"class":60},[54,2096,1571],{"class":64},[54,2098,2099,2101],{"class":56,"line":378},[54,2100,349],{"class":60},[54,2102,1571],{"class":64},[54,2104,2105,2107,2109,2111,2113,2115,2117],{"class":56,"line":387},[54,2106,390],{"class":60},[54,2108,2046],{"class":64},[54,2110,69],{"class":68},[54,2112,272],{"class":64},[54,2114,164],{"class":68},[54,2116,133],{"class":64},[54,2118,344],{"class":68},[54,2120,2121,2123],{"class":56,"line":1659},[54,2122,1650],{"class":60},[54,2124,1579],{"class":64},[31,2126,2127],{"id":2127},"递归展开数组",[13,2129,2130,2133,2134,1859,2137,301],{},[35,2131,2132],{},"Flatten","用于递归展开数组，将嵌套的数组转换为平铺的数组。例如",[35,2135,2136],{},"Flatten\u003C[1, [2, 3], 4, [5, 6]]]>",[35,2138,2139],{},"[1, 2, 3, 4, 5, 6]",[13,2141,2142,2143,2145,2146,2148],{},"实现方式和",[35,2144,1941],{},"类似，都是依次观察数组中每个元素是否是嵌套的，并递归地应用",[35,2147,2132],{},"类型。",[45,2150,2152],{"className":47,"code":2151,"language":49,"meta":50,"style":50},"type Flatten\u003CT extends any[]> = T extends []\n  ? []\n  : T extends [infer F, ...infer R]\n    ? F extends any[]\n      ? [...Flatten\u003CF>, ...Flatten\u003CR>]\n      : [F, ...Flatten\u003CR>]\n    : never\n",[35,2153,2154,2177,2183,2201,2214,2239,2257],{"__ignoreMap":50},[54,2155,2156,2158,2161,2163,2165,2167,2169,2171,2173,2175],{"class":56,"line":57},[54,2157,61],{"class":60},[54,2159,2160],{"class":64}," Flatten",[54,2162,69],{"class":68},[54,2164,72],{"class":64},[54,2166,174],{"class":60},[54,2168,464],{"class":64},[54,2170,1094],{"class":68},[54,2172,72],{"class":64},[54,2174,174],{"class":60},[54,2176,1420],{"class":68},[54,2178,2179,2181],{"class":56,"line":78},[54,2180,1627],{"class":60},[54,2182,1420],{"class":68},[54,2184,2185,2187,2189,2191,2193,2195,2197,2199],{"class":56,"line":112},[54,2186,1650],{"class":60},[54,2188,96],{"class":64},[54,2190,174],{"class":60},[54,2192,1146],{"class":68},[54,2194,843],{"class":64},[54,2196,1151],{"class":68},[54,2198,272],{"class":64},[54,2200,109],{"class":68},[54,2202,2203,2205,2207,2209,2211],{"class":56,"line":364},[54,2204,349],{"class":60},[54,2206,1162],{"class":64},[54,2208,174],{"class":60},[54,2210,464],{"class":64},[54,2212,2213],{"class":68},"[]\n",[54,2215,2216,2218,2221,2223,2225,2227,2230,2232,2234,2236],{"class":56,"line":378},[54,2217,367],{"class":60},[54,2219,2220],{"class":68}," [...",[54,2222,2132],{"class":64},[54,2224,69],{"class":68},[54,2226,843],{"class":64},[54,2228,2229],{"class":68},">, ...",[54,2231,2132],{"class":64},[54,2233,69],{"class":68},[54,2235,272],{"class":64},[54,2237,2238],{"class":68},">]\n",[54,2240,2241,2243,2245,2247,2249,2251,2253,2255],{"class":56,"line":387},[54,2242,381],{"class":60},[54,2244,84],{"class":68},[54,2246,843],{"class":64},[54,2248,1268],{"class":68},[54,2250,2132],{"class":64},[54,2252,69],{"class":68},[54,2254,272],{"class":64},[54,2256,2238],{"class":68},[54,2258,2259,2261],{"class":56,"line":1659},[54,2260,390],{"class":60},[54,2262,286],{"class":64},[13,2264,2265,2266,2269,2270,2273,2274,2277,2278,2280],{},"需要注意的是，由于",[35,2267,2268],{},"Flatten\u003C[]>","的结果应当为",[35,2271,2272],{},"[]","，且",[35,2275,2276],{},"[] extends [infer F, ...infer R]","的表达式为",[35,2279,1866],{},"，所以需要在递归中添加一个判断。",[31,2282,2284],{"id":2283},"递归展开数组-n-次","递归展开数组 N 次",[13,2286,734,2287,2289,2290,2293],{},[35,2288,2132],{},"的实现中，总是递归地展开数组。现要实现类型",[35,2291,2292],{},"FlattenDepth\u003CT, N>","，使得数组 T 被递归展开 N 层。即：",[45,2295,2297],{"className":47,"code":2296,"language":49,"meta":50,"style":50},"type A = FlattenDepth\u003C[1, 2, [3, 4], [[[5]]]], 2> \u002F\u002F [1, 2, 3, 4, [5]]. 展开 2 层\ntype B = FlattenDepth\u003C[1, 2, [3, 4], [[[5]]]]> \u002F\u002F [1, 2, 3, 4, [[5]]]. 展开默认 1 层\n",[35,2298,2299,2346],{"__ignoreMap":50},[54,2300,2301,2303,2305,2307,2310,2313,2315,2317,2319,2322,2325,2327,2330,2333,2336,2339,2341,2343],{"class":56,"line":57},[54,2302,61],{"class":60},[54,2304,1670],{"class":64},[54,2306,662],{"class":68},[54,2308,2309],{"class":64},"FlattenDepth",[54,2311,2312],{"class":68},"\u003C[",[54,2314,1684],{"class":608},[54,2316,164],{"class":68},[54,2318,1717],{"class":608},[54,2320,2321],{"class":68},", [",[54,2323,2324],{"class":608},"3",[54,2326,164],{"class":68},[54,2328,2329],{"class":608},"4",[54,2331,2332],{"class":68},"], [[[",[54,2334,2335],{"class":608},"5",[54,2337,2338],{"class":68},"]]]], ",[54,2340,1717],{"class":608},[54,2342,275],{"class":68},[54,2344,2345],{"class":677},"\u002F\u002F [1, 2, 3, 4, [5]]. 展开 2 层\n",[54,2347,2348,2350,2352,2354,2356,2358,2360,2362,2364,2366,2368,2370,2372,2374,2376,2379],{"class":56,"line":78},[54,2349,61],{"class":60},[54,2351,1703],{"class":64},[54,2353,662],{"class":68},[54,2355,2309],{"class":64},[54,2357,2312],{"class":68},[54,2359,1684],{"class":608},[54,2361,164],{"class":68},[54,2363,1717],{"class":608},[54,2365,2321],{"class":68},[54,2367,2324],{"class":608},[54,2369,164],{"class":68},[54,2371,2329],{"class":608},[54,2373,2332],{"class":68},[54,2375,2335],{"class":608},[54,2377,2378],{"class":68},"]]]]> ",[54,2380,2381],{"class":677},"\u002F\u002F [1, 2, 3, 4, [[5]]]. 展开默认 1 层\n",[13,2383,2384,2385,2387],{},"问题的关键在于如何实现递归次数的累计。显然，N 作为",[35,2386,635],{},"类型，无法像 JS 层面的变量那样直接进行增减操作。但是，在类型系统中，可以操作数组，通过向数组中增加元素的方式来实现递归次数的累计。只需要检查数组的长度是否等于 N 即可。",[13,2389,2390,2391,2393,2394,2396,2397,2400,2401,2404,2405,2407,2408,2411,2412,2414,2415,2417,2418,2420,2421,2423],{},"关键在于何时需要向计数数组",[35,2392,133],{},"中增加元素。自然是需要在递归展开时。为了搞清楚展开发生在哪一步，不妨先观察上面",[35,2395,2132],{},"的实现。如果",[35,2398,2399],{},"infer F","推断为",[35,2402,2403],{},"any[]","的子类型，那么需要对",[35,2406,843],{},"进行展开，因此调用",[35,2409,2410],{},"Flatten\u003CF>","的时候就是展开的时候。所以，在",[35,2413,2309],{},"的实现中，也应当在展开",[35,2416,843],{},"时向",[35,2419,133],{},"中增加元素。下面的代码中，以高亮行标出此处，向",[35,2422,133],{},"中增加任意元素（此处为 1）。",[45,2425,2428],{"className":47,"code":2426,"highlights":2427,"language":49,"meta":50,"style":50},"\u002F\u002F 初始化 U 参数，用于记录递归次数\ntype FlattenDepth\u003CT extends any[], N extends number = 1, U extends any[] = []>\n  \u002F\u002F 首先检查是否满足了迭代次数要求\n  = U['length'] extends N\n    ? T\n    : T extends [infer F, ...infer R]\n      ? F extends any[]\n        ? [...FlattenDepth\u003CF, N, [...U, 1]>, ...FlattenDepth\u003CR, N, U>]\n        : [F, ...FlattenDepth\u003CR, N, U>]\n      : T\n",[1665],[35,2429,2430,2435,2475,2480,2498,2504,2522,2534,2580,2606],{"__ignoreMap":50},[54,2431,2432],{"class":56,"line":57},[54,2433,2434],{"class":677},"\u002F\u002F 初始化 U 参数，用于记录递归次数\n",[54,2436,2437,2439,2442,2444,2446,2448,2450,2452,2455,2457,2460,2462,2464,2466,2468,2470,2472],{"class":56,"line":78},[54,2438,61],{"class":60},[54,2440,2441],{"class":64}," FlattenDepth",[54,2443,69],{"class":68},[54,2445,72],{"class":64},[54,2447,174],{"class":60},[54,2449,464],{"class":64},[54,2451,1252],{"class":68},[54,2453,2454],{"class":64},"N",[54,2456,174],{"class":60},[54,2458,2459],{"class":64}," number",[54,2461,662],{"class":68},[54,2463,1684],{"class":608},[54,2465,164],{"class":68},[54,2467,133],{"class":64},[54,2469,174],{"class":60},[54,2471,464],{"class":64},[54,2473,2474],{"class":68},"[] = []>\n",[54,2476,2477],{"class":56,"line":112},[54,2478,2479],{"class":677},"  \u002F\u002F 首先检查是否满足了迭代次数要求\n",[54,2481,2482,2485,2487,2489,2491,2493,2495],{"class":56,"line":364},[54,2483,2484],{"class":68},"  = ",[54,2486,133],{"class":64},[54,2488,104],{"class":68},[54,2490,1101],{"class":608},[54,2492,1157],{"class":68},[54,2494,137],{"class":60},[54,2496,2497],{"class":64}," N\n",[54,2499,2500,2502],{"class":56,"line":378},[54,2501,349],{"class":60},[54,2503,189],{"class":64},[54,2505,2506,2508,2510,2512,2514,2516,2518,2520],{"class":56,"line":387},[54,2507,390],{"class":60},[54,2509,96],{"class":64},[54,2511,174],{"class":60},[54,2513,1146],{"class":68},[54,2515,843],{"class":64},[54,2517,1151],{"class":68},[54,2519,272],{"class":64},[54,2521,109],{"class":68},[54,2523,2524,2526,2528,2530,2532],{"class":56,"line":1659},[54,2525,367],{"class":60},[54,2527,1162],{"class":64},[54,2529,174],{"class":60},[54,2531,464],{"class":64},[54,2533,2213],{"class":68},[54,2535,2538,2540,2542,2544,2546,2548,2550,2552,2555,2557,2559,2561,2564,2566,2568,2570,2572,2574,2576,2578],{"class":2536,"line":1665},[56,2537],"highlight",[54,2539,1568],{"class":60},[54,2541,2220],{"class":68},[54,2543,2309],{"class":64},[54,2545,69],{"class":68},[54,2547,843],{"class":64},[54,2549,164],{"class":68},[54,2551,2454],{"class":64},[54,2553,2554],{"class":68},", [...",[54,2556,133],{"class":64},[54,2558,164],{"class":68},[54,2560,1684],{"class":608},[54,2562,2563],{"class":68},"]>, ...",[54,2565,2309],{"class":64},[54,2567,69],{"class":68},[54,2569,272],{"class":64},[54,2571,164],{"class":68},[54,2573,2454],{"class":64},[54,2575,164],{"class":68},[54,2577,133],{"class":64},[54,2579,2238],{"class":68},[54,2581,2582,2584,2586,2588,2590,2592,2594,2596,2598,2600,2602,2604],{"class":56,"line":1692},[54,2583,1576],{"class":60},[54,2585,84],{"class":68},[54,2587,843],{"class":64},[54,2589,1268],{"class":68},[54,2591,2309],{"class":64},[54,2593,69],{"class":68},[54,2595,272],{"class":64},[54,2597,164],{"class":68},[54,2599,2454],{"class":64},[54,2601,164],{"class":68},[54,2603,133],{"class":64},[54,2605,2238],{"class":68},[54,2607,2608,2610],{"class":56,"line":1698},[54,2609,381],{"class":60},[54,2611,189],{"class":64},[2613,2614,2615],"style",{},"html pre.shiki code .sqF6k, html code.shiki .sqF6k{--shiki-default:#54B9FF}html pre.shiki code .s8HWQ, html code.shiki .s8HWQ{--shiki-default:#ACAFFF}html pre.shiki code .sLo_3, html code.shiki .sLo_3{--shiki-default:#EEF0F9}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .smONe, html code.shiki .smONe{--shiki-default:#4BF3C8;--shiki-default-font-style:italic}html pre.shiki code .stHQG, html code.shiki .stHQG{--shiki-default:#00DAEF}html pre.shiki code .sb16X, html code.shiki .sb16X{--shiki-default:#4BF3C8}html pre.shiki code .se2J0, html code.shiki .se2J0{--shiki-default:#FFD493}html pre.shiki code .sy89t, html code.shiki .sy89t{--shiki-default:#EEF0F98F;--shiki-default-font-style:italic}",{"title":50,"searchDepth":78,"depth":78,"links":2617},[2618,2626],{"id":25,"depth":78,"text":26,"children":2619},[2620,2621,2622,2623,2625],{"id":33,"depth":112,"text":37},{"id":118,"depth":112,"text":121},{"id":208,"depth":112,"text":211},{"id":395,"depth":112,"text":2624},"Parameters和ReturnType",{"id":703,"depth":112,"text":706},{"id":818,"depth":78,"text":818,"children":2627},[2628,2629,2630,2631,2632,2633,2635,2637,2638,2639,2640],{"id":824,"depth":112,"text":827},{"id":893,"depth":112,"text":893},{"id":1051,"depth":112,"text":1051},{"id":1106,"depth":112,"text":1107},{"id":1212,"depth":112,"text":1215},{"id":1275,"depth":112,"text":2634},"Push和Unshift",{"id":1358,"depth":112,"text":2636},"Pop和Shift",{"id":1462,"depth":112,"text":1465},{"id":1938,"depth":112,"text":1941},{"id":2127,"depth":112,"text":2127},{"id":2283,"depth":112,"text":2284},"2022.07.7","md","借助第一篇的基础工具实现一些基本类型，包括部分内置类型和类型体操的常用类型",{},"\u002Fwriting\u002Ftype-gymnastics-2",{"title":5,"description":50},"writing\u002Ftype-gymnastics-2",[2649],"TypeScript","fq84Vpg05vRCoQZ2j4MQkLrYNKk8f7pLF2uWr2pgrZc",1774950409696]