[{"data":1,"prerenderedAt":4671},["ShallowReactive",2],{"\u002Fposts\u002Ftypescript-development-tips":3,"surround:\u002Fposts\u002Ftypescript-development-tips":4662},{"id":4,"title":5,"body":6,"createdAt":4655,"description":79,"extension":4656,"meta":4657,"navigation":178,"path":4658,"seo":4659,"stem":4660,"__hash__":4661},"posts\u002Fposts\u002F2020\u002Ftypescript-development-tips.md","TypeScript 开发技巧",{"type":7,"value":8,"toc":4627},"minimark",[9,13,18,28,31,35,38,51,58,66,70,73,262,273,276,376,382,387,394,459,462,466,473,625,632,774,777,780,794,857,875,935,942,984,1006,1015,1019,1022,1087,1091,1098,1102,1109,1124,1225,1254,1270,1282,1376,1392,1414,1478,1483,1499,1516,1622,1634,1638,1641,1691,1699,1702,1707,1757,1764,1772,1811,1818,1821,1829,1832,1839,1873,1876,1879,1882,1951,1954,1995,1998,2035,2041,2073,2077,2084,2115,2133,2139,2171,2182,2186,2189,2192,2267,2270,2301,2304,2327,2337,2341,2353,2487,2501,2521,2531,2554,2560,2583,2586,2653,2656,4623],[10,11,5],"h1",{"id":12},"typescript-开发技巧",[14,15,17],"h2",{"id":16},"intro","写在前面",[19,20,21,22],"p",{},"本文假设读者有基本的 TypeScript 常识，如果你对于 TypeScript 本身完全不了解，建议先阅读 ",[23,24,25],"a",{"href":25,"rel":26},"https:\u002F\u002Fwww.typescriptlang.org\u002Fdocs\u002Fhandbook\u002Fbasic-types.html",[27],"nofollow",[19,29,30],{},"本文不包含体操项目，如有需要请自行锻炼。",[14,32,34],{"id":33},"why","为啥要用 TypeScript ？",[19,36,37],{},"这可能是最老生常谈的一个问题。类型系统的好处有很多：",[39,40,41,45,48],"ul",{},[42,43,44],"li",{},"可读性",[42,46,47],{},"工具链（编辑器自动提示、更精确的 Lint 等）",[42,49,50],{},"重构（例如，想要给某个变量改名，又怕漏掉）",[19,52,53,54],{},"以上好处都很明显，但同时，类型系统也有一些潜在的好处。例如，当你发现你的类型无法利用 TypeScript 声明时，一定程度上说明接口的设计是类型不友好的，或者说比较晦涩\u002F容易出错，例如，",[23,55,57],{"href":56},"mailto:Vue@2.x","Vue@2.x",[19,59,60,61,65],{},"但一定得注意：",[62,63,64],"strong",{},"TS 不会做任何运行时的校验","，所以如果你的代码最终会被编译成 JS，又有可能被 JS 代码调用，还是需要做好代码防御的。",[14,67,69],{"id":68},"how","怎么写类型",[19,71,72],{},"最简单的，大家都会写的形式，就是声明+冒号+类型：",[74,75,80],"pre",{"className":76,"code":77,"language":78,"meta":79,"style":79},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F code.js\nconst num = 1;\nfunction setNum(value, defaultValue = 0) {\n  num = value || defaultValue;\n}\n\n\u002F\u002F code.ts\nconst num: number = 1;\nfunction setNum(value: number, defaultValue: number = 0): void {\n  num = value || defaultValue;\n}\n","ts","",[81,82,83,92,114,149,167,173,180,186,207,242,257],"code",{"__ignoreMap":79},[84,85,88],"span",{"class":86,"line":87},"line",1,[84,89,91],{"class":90},"sHwdD","\u002F\u002F code.js\n",[84,93,95,99,103,107,111],{"class":86,"line":94},2,[84,96,98],{"class":97},"spNyl","const",[84,100,102],{"class":101},"sTEyZ"," num ",[84,104,106],{"class":105},"sMK4o","=",[84,108,110],{"class":109},"sbssI"," 1",[84,112,113],{"class":105},";\n",[84,115,117,120,124,127,131,134,137,140,143,146],{"class":86,"line":116},3,[84,118,119],{"class":97},"function",[84,121,123],{"class":122},"s2Zo4"," setNum",[84,125,126],{"class":105},"(",[84,128,130],{"class":129},"sHdIc","value",[84,132,133],{"class":105},",",[84,135,136],{"class":129}," defaultValue",[84,138,139],{"class":105}," =",[84,141,142],{"class":109}," 0",[84,144,145],{"class":105},")",[84,147,148],{"class":105}," {\n",[84,150,152,155,157,160,163,165],{"class":86,"line":151},4,[84,153,154],{"class":101},"  num",[84,156,139],{"class":105},[84,158,159],{"class":101}," value",[84,161,162],{"class":105}," ||",[84,164,136],{"class":101},[84,166,113],{"class":105},[84,168,170],{"class":86,"line":169},5,[84,171,172],{"class":105},"}\n",[84,174,176],{"class":86,"line":175},6,[84,177,179],{"emptyLinePlaceholder":178},true,"\n",[84,181,183],{"class":86,"line":182},7,[84,184,185],{"class":90},"\u002F\u002F code.ts\n",[84,187,189,191,194,197,201,203,205],{"class":86,"line":188},8,[84,190,98],{"class":97},[84,192,193],{"class":101}," num",[84,195,196],{"class":105},":",[84,198,200],{"class":199},"sBMFI"," number",[84,202,139],{"class":105},[84,204,110],{"class":109},[84,206,113],{"class":105},[84,208,210,212,214,216,218,220,222,224,226,228,230,232,234,237,240],{"class":86,"line":209},9,[84,211,119],{"class":97},[84,213,123],{"class":122},[84,215,126],{"class":105},[84,217,130],{"class":129},[84,219,196],{"class":105},[84,221,200],{"class":199},[84,223,133],{"class":105},[84,225,136],{"class":129},[84,227,196],{"class":105},[84,229,200],{"class":199},[84,231,139],{"class":105},[84,233,142],{"class":109},[84,235,236],{"class":105},"):",[84,238,239],{"class":199}," void",[84,241,148],{"class":105},[84,243,245,247,249,251,253,255],{"class":86,"line":244},10,[84,246,154],{"class":101},[84,248,139],{"class":105},[84,250,159],{"class":101},[84,252,162],{"class":105},[84,254,136],{"class":101},[84,256,113],{"class":105},[84,258,260],{"class":86,"line":259},11,[84,261,172],{"class":105},[19,263,264,265,268,269,272],{},"简单地说，就是在任何",[62,266,267],{},"声明变量","的地方（import 除外），以及",[62,270,271],{},"函数返回值","，在变量名的后面加上 :类型，就完事儿了！",[19,274,275],{},"对于函数而言，一种方式是通过参数和默认值声明，就像上面一样。同时你也可以写一个声明语句，例如：",[74,277,279],{"className":76,"code":278,"language":78,"meta":79,"style":79},"const num: number = 1;\n\nfunction function(value: number, defaultValue?: number): void\n\nfunction setNum(value, defaultValue = 0): void {\n  num = value || defaultValue;\n}\n",[81,280,281,297,301,330,334,358,372],{"__ignoreMap":79},[84,282,283,285,287,289,291,293,295],{"class":86,"line":87},[84,284,98],{"class":97},[84,286,193],{"class":101},[84,288,196],{"class":105},[84,290,200],{"class":199},[84,292,139],{"class":105},[84,294,110],{"class":109},[84,296,113],{"class":105},[84,298,299],{"class":86,"line":94},[84,300,179],{"emptyLinePlaceholder":178},[84,302,303,305,308,310,312,314,316,318,320,323,325,327],{"class":86,"line":116},[84,304,119],{"class":97},[84,306,307],{"class":122}," function",[84,309,126],{"class":105},[84,311,130],{"class":129},[84,313,196],{"class":105},[84,315,200],{"class":199},[84,317,133],{"class":105},[84,319,136],{"class":129},[84,321,322],{"class":105},"?:",[84,324,200],{"class":199},[84,326,236],{"class":105},[84,328,329],{"class":199}," void\n",[84,331,332],{"class":86,"line":151},[84,333,179],{"emptyLinePlaceholder":178},[84,335,336,338,340,342,344,346,348,350,352,354,356],{"class":86,"line":169},[84,337,119],{"class":97},[84,339,123],{"class":122},[84,341,126],{"class":105},[84,343,130],{"class":129},[84,345,133],{"class":105},[84,347,136],{"class":129},[84,349,139],{"class":105},[84,351,142],{"class":109},[84,353,236],{"class":105},[84,355,239],{"class":199},[84,357,148],{"class":105},[84,359,360,362,364,366,368,370],{"class":86,"line":175},[84,361,154],{"class":101},[84,363,139],{"class":105},[84,365,159],{"class":101},[84,367,162],{"class":105},[84,369,136],{"class":101},[84,371,113],{"class":105},[84,373,374],{"class":86,"line":182},[84,375,172],{"class":105},[377,378,379],"blockquote",{},[19,380,381],{},"上面的 ?: 代表这个参数可以不传的意思",[383,384,386],"h3",{"id":385},"type-inference","类型推导",[19,388,389,390,393],{},"如果只是这样，那 TS 就太辣鸡了。TS 的一个比较优越的地方就是，如果我们假设代码是",[62,391,392],{},"静态类型","的，那么很多地方的类型就不需要我们自己声明了，就像 Java 中也有类似的机制。所以上面的代码其实只要这样就可以了：",[74,395,397],{"className":76,"code":396,"language":78,"meta":79,"style":79},"\u002F\u002F code.ts\nconst num = 1;\nfunction setNum(value: number, defaultValue = 0) {\n  num = value || defaultValue;\n}\n",[81,398,399,403,415,441,455],{"__ignoreMap":79},[84,400,401],{"class":86,"line":87},[84,402,185],{"class":90},[84,404,405,407,409,411,413],{"class":86,"line":94},[84,406,98],{"class":97},[84,408,102],{"class":101},[84,410,106],{"class":105},[84,412,110],{"class":109},[84,414,113],{"class":105},[84,416,417,419,421,423,425,427,429,431,433,435,437,439],{"class":86,"line":116},[84,418,119],{"class":97},[84,420,123],{"class":122},[84,422,126],{"class":105},[84,424,130],{"class":129},[84,426,196],{"class":105},[84,428,200],{"class":199},[84,430,133],{"class":105},[84,432,136],{"class":129},[84,434,139],{"class":105},[84,436,142],{"class":109},[84,438,145],{"class":105},[84,440,148],{"class":105},[84,442,443,445,447,449,451,453],{"class":86,"line":151},[84,444,154],{"class":101},[84,446,139],{"class":105},[84,448,159],{"class":101},[84,450,162],{"class":105},[84,452,136],{"class":101},[84,454,113],{"class":105},[84,456,457],{"class":86,"line":169},[84,458,172],{"class":105},[19,460,461],{},"（注意 value 这个参数的类型无论如何也推导不出来，所以还是要自己声明）",[383,463,465],{"id":464},"objects-and-functions","对象和函数类型",[19,467,468,469,472],{},"TS 也提供了 ",[81,470,471],{},"interface"," 语法来声明一些非 JS 内置的类型，例如",[74,474,476],{"className":76,"code":475,"language":78,"meta":79,"style":79},"interface MyObject {\n  key: string;\n  name: string;\n  value: number;\n}\n\ninterface MyFunction {\n  (value: number, defaultValue: number): void;\n  staticProp: number;\n}\n\ninterface MyClassInterface {\n  constructor(value: number): MyClass;\n  \u002F\u002F 这里有一种老的写法是 new (value: number)，不过现在不太建议这么写了，类型匹配会有困难\n}\n",[81,477,478,487,500,511,522,526,530,539,564,575,579,583,593,614,620],{"__ignoreMap":79},[84,479,480,482,485],{"class":86,"line":87},[84,481,471],{"class":97},[84,483,484],{"class":199}," MyObject",[84,486,148],{"class":105},[84,488,489,493,495,498],{"class":86,"line":94},[84,490,492],{"class":491},"swJcz","  key",[84,494,196],{"class":105},[84,496,497],{"class":199}," string",[84,499,113],{"class":105},[84,501,502,505,507,509],{"class":86,"line":116},[84,503,504],{"class":491},"  name",[84,506,196],{"class":105},[84,508,497],{"class":199},[84,510,113],{"class":105},[84,512,513,516,518,520],{"class":86,"line":151},[84,514,515],{"class":491},"  value",[84,517,196],{"class":105},[84,519,200],{"class":199},[84,521,113],{"class":105},[84,523,524],{"class":86,"line":169},[84,525,172],{"class":105},[84,527,528],{"class":86,"line":175},[84,529,179],{"emptyLinePlaceholder":178},[84,531,532,534,537],{"class":86,"line":182},[84,533,471],{"class":97},[84,535,536],{"class":199}," MyFunction",[84,538,148],{"class":105},[84,540,541,544,546,548,550,552,554,556,558,560,562],{"class":86,"line":188},[84,542,543],{"class":105},"  (",[84,545,130],{"class":129},[84,547,196],{"class":105},[84,549,200],{"class":199},[84,551,133],{"class":105},[84,553,136],{"class":129},[84,555,196],{"class":105},[84,557,200],{"class":199},[84,559,236],{"class":105},[84,561,239],{"class":199},[84,563,113],{"class":105},[84,565,566,569,571,573],{"class":86,"line":209},[84,567,568],{"class":491},"  staticProp",[84,570,196],{"class":105},[84,572,200],{"class":199},[84,574,113],{"class":105},[84,576,577],{"class":86,"line":244},[84,578,172],{"class":105},[84,580,581],{"class":86,"line":259},[84,582,179],{"emptyLinePlaceholder":178},[84,584,586,588,591],{"class":86,"line":585},12,[84,587,471],{"class":97},[84,589,590],{"class":199}," MyClassInterface",[84,592,148],{"class":105},[84,594,596,599,601,603,605,607,609,612],{"class":86,"line":595},13,[84,597,598],{"class":97},"  constructor",[84,600,126],{"class":105},[84,602,130],{"class":129},[84,604,196],{"class":105},[84,606,200],{"class":199},[84,608,236],{"class":105},[84,610,611],{"class":199}," MyClass",[84,613,113],{"class":105},[84,615,617],{"class":86,"line":616},14,[84,618,619],{"class":90},"  \u002F\u002F 这里有一种老的写法是 new (value: number)，不过现在不太建议这么写了，类型匹配会有困难\n",[84,621,623],{"class":86,"line":622},15,[84,624,172],{"class":105},[19,626,627,628,631],{},"同时也有一个 ",[81,629,630],{},"type"," 关键字，上面的类型也可以写成",[74,633,635],{"className":76,"code":634,"language":78,"meta":79,"style":79},"type MyObject =  {\n  key: string;\n  name: string;\n  value: number;\n}\n\ntype MyFunction = (value: number, defaultValue: number) => void & { staticProp: number };\n\ntype MyClass = {\n  constructor(value: number): MyClass;\n  \u002F\u002F 这里有一种老的写法是 new (value: number)，不过现在不太建议这么写了，类型匹配会有困难\n}\n",[81,636,637,648,658,668,678,682,686,734,738,748,766,770],{"__ignoreMap":79},[84,638,639,641,643,645],{"class":86,"line":87},[84,640,630],{"class":97},[84,642,484],{"class":199},[84,644,139],{"class":105},[84,646,647],{"class":105},"  {\n",[84,649,650,652,654,656],{"class":86,"line":94},[84,651,492],{"class":491},[84,653,196],{"class":105},[84,655,497],{"class":199},[84,657,113],{"class":105},[84,659,660,662,664,666],{"class":86,"line":116},[84,661,504],{"class":491},[84,663,196],{"class":105},[84,665,497],{"class":199},[84,667,113],{"class":105},[84,669,670,672,674,676],{"class":86,"line":151},[84,671,515],{"class":491},[84,673,196],{"class":105},[84,675,200],{"class":199},[84,677,113],{"class":105},[84,679,680],{"class":86,"line":169},[84,681,172],{"class":105},[84,683,684],{"class":86,"line":175},[84,685,179],{"emptyLinePlaceholder":178},[84,687,688,690,692,694,697,699,701,703,705,707,709,711,713,716,718,721,724,727,729,731],{"class":86,"line":182},[84,689,630],{"class":97},[84,691,536],{"class":199},[84,693,139],{"class":105},[84,695,696],{"class":105}," (",[84,698,130],{"class":129},[84,700,196],{"class":105},[84,702,200],{"class":199},[84,704,133],{"class":105},[84,706,136],{"class":129},[84,708,196],{"class":105},[84,710,200],{"class":199},[84,712,145],{"class":105},[84,714,715],{"class":97}," =>",[84,717,239],{"class":199},[84,719,720],{"class":105}," &",[84,722,723],{"class":105}," {",[84,725,726],{"class":491}," staticProp",[84,728,196],{"class":105},[84,730,200],{"class":199},[84,732,733],{"class":105}," };\n",[84,735,736],{"class":86,"line":188},[84,737,179],{"emptyLinePlaceholder":178},[84,739,740,742,744,746],{"class":86,"line":209},[84,741,630],{"class":97},[84,743,611],{"class":199},[84,745,139],{"class":105},[84,747,148],{"class":105},[84,749,750,752,754,756,758,760,762,764],{"class":86,"line":244},[84,751,598],{"class":97},[84,753,126],{"class":105},[84,755,130],{"class":129},[84,757,196],{"class":105},[84,759,200],{"class":199},[84,761,236],{"class":105},[84,763,611],{"class":199},[84,765,113],{"class":105},[84,767,768],{"class":86,"line":259},[84,769,619],{"class":90},[84,771,772],{"class":86,"line":585},[84,773,172],{"class":105},[19,775,776],{},"（上面这些换行前的符号写成逗号也是一样的，神奇吧？）",[19,778,779],{},"两者看上去都可以完成一样的事情，事实也基本如此。但是还是会有一些细微的差别：",[39,781,782],{},[42,783,784,786,787,790,791,793],{},[81,785,471],{}," 有一种 ",[81,788,789],{},"implements"," 的用法（下面这个例子实际上 ",[81,792,630],{}," 可以通过改写成赋值语句的形式完成）",[74,795,797],{"className":76,"code":796,"language":78,"meta":79,"style":79},"interface MyClassInterface {\n  setValue(value: number): void;\n}\n\nclass MyClass implements MyClassInterface {\n  \u002F\u002F 如果没有 setValue 就会报错\n}\n",[81,798,799,807,826,830,834,848,853],{"__ignoreMap":79},[84,800,801,803,805],{"class":86,"line":87},[84,802,471],{"class":97},[84,804,590],{"class":199},[84,806,148],{"class":105},[84,808,809,812,814,816,818,820,822,824],{"class":86,"line":94},[84,810,811],{"class":491},"  setValue",[84,813,126],{"class":105},[84,815,130],{"class":129},[84,817,196],{"class":105},[84,819,200],{"class":199},[84,821,236],{"class":105},[84,823,239],{"class":199},[84,825,113],{"class":105},[84,827,828],{"class":86,"line":116},[84,829,172],{"class":105},[84,831,832],{"class":86,"line":151},[84,833,179],{"emptyLinePlaceholder":178},[84,835,836,839,841,844,846],{"class":86,"line":169},[84,837,838],{"class":97},"class",[84,840,611],{"class":199},[84,842,843],{"class":97}," implements",[84,845,590],{"class":199},[84,847,148],{"class":105},[84,849,850],{"class":86,"line":175},[84,851,852],{"class":90},"  \u002F\u002F 如果没有 setValue 就会报错\n",[84,854,855],{"class":86,"line":182},[84,856,172],{"class":105},[39,858,859],{},[42,860,861,863,864,867,868,870,871,874],{},[81,862,471],{}," 可以 ",[81,865,866],{},"extends","（下面这个例子实际上 ",[81,869,630],{}," 可以通过 ",[81,872,873],{},"&"," 运算完成）",[74,876,878],{"className":76,"code":877,"language":78,"meta":79,"style":79},"interface A {\n  key: string;\n}\n\ninterface B extends A {\n  name: string;\n}\n",[81,879,880,889,899,903,907,921,931],{"__ignoreMap":79},[84,881,882,884,887],{"class":86,"line":87},[84,883,471],{"class":97},[84,885,886],{"class":199}," A",[84,888,148],{"class":105},[84,890,891,893,895,897],{"class":86,"line":94},[84,892,492],{"class":491},[84,894,196],{"class":105},[84,896,497],{"class":199},[84,898,113],{"class":105},[84,900,901],{"class":86,"line":116},[84,902,172],{"class":105},[84,904,905],{"class":86,"line":151},[84,906,179],{"emptyLinePlaceholder":178},[84,908,909,911,914,917,919],{"class":86,"line":169},[84,910,471],{"class":97},[84,912,913],{"class":199}," B",[84,915,916],{"class":97}," extends",[84,918,886],{"class":199},[84,920,148],{"class":105},[84,922,923,925,927,929],{"class":86,"line":175},[84,924,504],{"class":491},[84,926,196],{"class":105},[84,928,497],{"class":199},[84,930,113],{"class":105},[84,932,933],{"class":86,"line":182},[84,934,172],{"class":105},[39,936,937],{},[42,938,939,941],{},[81,940,630],{}," 可以把类型声明成基本类型，还可以使用类型运算",[74,943,945],{"className":76,"code":944,"language":78,"meta":79,"style":79},"type MyProp = string\n\ntype MyKey = string | number | symbol\n",[81,946,947,959,963],{"__ignoreMap":79},[84,948,949,951,954,956],{"class":86,"line":87},[84,950,630],{"class":97},[84,952,953],{"class":199}," MyProp",[84,955,139],{"class":105},[84,957,958],{"class":199}," string\n",[84,960,961],{"class":86,"line":94},[84,962,179],{"emptyLinePlaceholder":178},[84,964,965,967,970,972,974,977,979,981],{"class":86,"line":116},[84,966,630],{"class":97},[84,968,969],{"class":199}," MyKey",[84,971,139],{"class":105},[84,973,497],{"class":199},[84,975,976],{"class":105}," |",[84,978,200],{"class":199},[84,980,976],{"class":105},[84,982,983],{"class":199}," symbol\n",[39,985,986,996],{},[42,987,988,990,991],{},[81,989,471],{}," 可以用",[23,992,995],{"href":993,"rel":994},"https:\u002F\u002Fbytedance.feishu.cn\u002Fwiki\u002FwikcnNlnC6xWMrIAVB1LnWmlgJb#jxyS1D",[27],"索引签名",[42,997,998,1000,1001],{},[81,999,630],{}," 可以用更高级的",[23,1002,1005],{"href":1003,"rel":1004},"https:\u002F\u002Fbytedance.feishu.cn\u002Fwiki\u002FwikcnNlnC6xWMrIAVB1LnWmlgJb#n5bUkT",[27],"类型参数",[19,1007,1008,1009,1011,1012,1014],{},"从实现上来说，",[81,1010,471],{}," 是声明了新的类型，而 ",[81,1013,630],{}," 只是类型的别名，有一点点像值和引用的区别。",[383,1016,1018],{"id":1017},"constants","常量类型",[19,1020,1021],{},"TS 的类型有时候如同你想象中一样工作，所以，如果想不出来的话，可以写下来试试",[74,1023,1025],{"className":76,"code":1024,"language":78,"meta":79,"style":79},"type A = true \u002F\u002F A 类型的变量就必须得是 true\ntype B = 'oops' \u002F\u002F B 类型的变量就必须得是 oops\ntype C = [string, number] \u002F\u002F C 类型的变量就必须得是一个数组，必须得有两个元素，必须分别是 string 和 number\n",[81,1026,1027,1041,1062],{"__ignoreMap":79},[84,1028,1029,1031,1033,1035,1038],{"class":86,"line":87},[84,1030,630],{"class":97},[84,1032,886],{"class":199},[84,1034,139],{"class":105},[84,1036,1037],{"class":199}," true",[84,1039,1040],{"class":90}," \u002F\u002F A 类型的变量就必须得是 true\n",[84,1042,1043,1045,1047,1049,1052,1056,1059],{"class":86,"line":94},[84,1044,630],{"class":97},[84,1046,913],{"class":199},[84,1048,139],{"class":105},[84,1050,1051],{"class":105}," '",[84,1053,1055],{"class":1054},"sfazB","oops",[84,1057,1058],{"class":105},"'",[84,1060,1061],{"class":90}," \u002F\u002F B 类型的变量就必须得是 oops\n",[84,1063,1064,1066,1069,1071,1074,1077,1079,1081,1084],{"class":86,"line":116},[84,1065,630],{"class":97},[84,1067,1068],{"class":199}," C",[84,1070,139],{"class":105},[84,1072,1073],{"class":101}," [",[84,1075,1076],{"class":199},"string",[84,1078,133],{"class":105},[84,1080,200],{"class":199},[84,1082,1083],{"class":101},"] ",[84,1085,1086],{"class":90},"\u002F\u002F C 类型的变量就必须得是一个数组，必须得有两个元素，必须分别是 string 和 number\n",[383,1088,1090],{"id":1089},"enums","枚举",[19,1092,1093,1094],{},"没啥可说的，自己看文档就好了 ",[23,1095,1096],{"href":1096,"rel":1097},"https:\u002F\u002Fwww.typescriptlang.org\u002Fdocs\u002Fhandbook\u002Fbasic-types.html#enum",[27],[383,1099,1101],{"id":1100},"utilities","奇奇怪怪的类型",[19,1103,1104,1105,1108],{},"最经典的大概就是 ",[81,1106,1107],{},"any"," 了。鲁迅曾经说过，如果你不知道怎么写 TS 类型，那就写 any 就完事了。当然实际上这样做会被打的。",[19,1110,1111,1113,1114,1123],{},[81,1112,1107],{}," 是顶层类型也是底层类型，也就是说，",[62,1115,1116,1117,1119,1120,1122],{},"啥都是 ",[81,1118,1107],{},"，",[81,1121,1107],{}," 也啥都是","。比方说",[74,1125,1127],{"className":76,"code":1126,"language":78,"meta":79,"style":79},"\u002F\u002F 啥都是 any\nfunction myFunction(value: any) {}\nlet v: number\nmyFunction(v) \u002F\u002F OK!\n\n\u002F\u002F any 啥都是\nfunction myFunction(value: number) {}\nlet v: any\nmyFunction(v) \u002F\u002F OK!\n",[81,1128,1129,1134,1155,1168,1179,1183,1188,1206,1217],{"__ignoreMap":79},[84,1130,1131],{"class":86,"line":87},[84,1132,1133],{"class":90},"\u002F\u002F 啥都是 any\n",[84,1135,1136,1138,1141,1143,1145,1147,1150,1152],{"class":86,"line":94},[84,1137,119],{"class":97},[84,1139,1140],{"class":122}," myFunction",[84,1142,126],{"class":105},[84,1144,130],{"class":129},[84,1146,196],{"class":105},[84,1148,1149],{"class":199}," any",[84,1151,145],{"class":105},[84,1153,1154],{"class":105}," {}\n",[84,1156,1157,1160,1163,1165],{"class":86,"line":116},[84,1158,1159],{"class":97},"let",[84,1161,1162],{"class":101}," v",[84,1164,196],{"class":105},[84,1166,1167],{"class":199}," number\n",[84,1169,1170,1173,1176],{"class":86,"line":151},[84,1171,1172],{"class":122},"myFunction",[84,1174,1175],{"class":101},"(v) ",[84,1177,1178],{"class":90},"\u002F\u002F OK!\n",[84,1180,1181],{"class":86,"line":169},[84,1182,179],{"emptyLinePlaceholder":178},[84,1184,1185],{"class":86,"line":175},[84,1186,1187],{"class":90},"\u002F\u002F any 啥都是\n",[84,1189,1190,1192,1194,1196,1198,1200,1202,1204],{"class":86,"line":182},[84,1191,119],{"class":97},[84,1193,1140],{"class":122},[84,1195,126],{"class":105},[84,1197,130],{"class":129},[84,1199,196],{"class":105},[84,1201,200],{"class":199},[84,1203,145],{"class":105},[84,1205,1154],{"class":105},[84,1207,1208,1210,1212,1214],{"class":86,"line":188},[84,1209,1159],{"class":97},[84,1211,1162],{"class":101},[84,1213,196],{"class":105},[84,1215,1216],{"class":199}," any\n",[84,1218,1219,1221,1223],{"class":86,"line":209},[84,1220,1172],{"class":122},[84,1222,1175],{"class":101},[84,1224,1178],{"class":90},[19,1226,1227,1228,1234,1235,1240,1241,1243,1244,1246,1247,1250,1251,1253],{},"也因为如此，",[62,1229,1230,1231,1233],{},"使用 ",[81,1232,1107],{}," 等于放弃了类型","。并且很重要的是，",[62,1236,1237,1239],{},[81,1238,1107],{}," 具有传播性","，也就是说，",[81,1242,1107],{}," 和任何类型的联合类型都是 ",[81,1245,1107],{},"，比如 ",[81,1248,1249],{},"any | number","。所以，尽量避免使用 ",[81,1252,1107],{},"，除非明确地知道就是没有类型",[19,1255,1256,1257,1259,1260,1263,1264,1266,1267,1269],{},"除了 ",[81,1258,1107],{}," 之外另一个常见的类型是 ",[81,1261,1262],{},"unknown","，这俩有点像，但是 ",[81,1265,1262],{}," 的提出就是为了解决 ",[81,1268,1107],{}," 的各种问题的。",[19,1271,1272,1274,1275,1123],{},[81,1273,1262],{}," 是顶层类型，但不是底层类型，也就是说，",[62,1276,1116,1277,1119,1279,1281],{},[81,1278,1262],{},[81,1280,1262],{}," 啥也不是",[74,1283,1285],{"className":76,"code":1284,"language":78,"meta":79,"style":79},"\u002F\u002F 啥都是 unknown\nfunction myFunction(value: unknown) {}\nlet v: number\nmyFunction(v) \u002F\u002F OK!\n\n\u002F\u002F unknown 啥也不是\nfunction myFunction(value: number) {}\nlet v: unknown\nmyFunction(v) \u002F\u002F TS error\n",[81,1286,1287,1292,1311,1321,1329,1333,1338,1356,1367],{"__ignoreMap":79},[84,1288,1289],{"class":86,"line":87},[84,1290,1291],{"class":90},"\u002F\u002F 啥都是 unknown\n",[84,1293,1294,1296,1298,1300,1302,1304,1307,1309],{"class":86,"line":94},[84,1295,119],{"class":97},[84,1297,1140],{"class":122},[84,1299,126],{"class":105},[84,1301,130],{"class":129},[84,1303,196],{"class":105},[84,1305,1306],{"class":199}," unknown",[84,1308,145],{"class":105},[84,1310,1154],{"class":105},[84,1312,1313,1315,1317,1319],{"class":86,"line":116},[84,1314,1159],{"class":97},[84,1316,1162],{"class":101},[84,1318,196],{"class":105},[84,1320,1167],{"class":199},[84,1322,1323,1325,1327],{"class":86,"line":151},[84,1324,1172],{"class":122},[84,1326,1175],{"class":101},[84,1328,1178],{"class":90},[84,1330,1331],{"class":86,"line":169},[84,1332,179],{"emptyLinePlaceholder":178},[84,1334,1335],{"class":86,"line":175},[84,1336,1337],{"class":90},"\u002F\u002F unknown 啥也不是\n",[84,1339,1340,1342,1344,1346,1348,1350,1352,1354],{"class":86,"line":182},[84,1341,119],{"class":97},[84,1343,1140],{"class":122},[84,1345,126],{"class":105},[84,1347,130],{"class":129},[84,1349,196],{"class":105},[84,1351,200],{"class":199},[84,1353,145],{"class":105},[84,1355,1154],{"class":105},[84,1357,1358,1360,1362,1364],{"class":86,"line":188},[84,1359,1159],{"class":97},[84,1361,1162],{"class":101},[84,1363,196],{"class":105},[84,1365,1366],{"class":199}," unknown\n",[84,1368,1369,1371,1373],{"class":86,"line":209},[84,1370,1172],{"class":122},[84,1372,1175],{"class":101},[84,1374,1375],{"class":90},"\u002F\u002F TS error\n",[19,1377,1378,1379,1381,1382,1384,1385,1388,1389,1391],{},"尽管 ",[81,1380,1262],{}," 同样有传播性，但是“unknown 啥也不是”限制了它实际使用时必须要强制类型声明，通常来说，这意味着 ",[81,1383,1262],{}," 不太会被滥用。一般来说，如果你发现一个",[62,1386,1387],{},"函数的返回值没用到","，那么就用 ",[81,1390,1262],{}," 就对了。",[19,1393,1394,1397,1398,1400,1401,1410,1411,1413],{},[81,1395,1396],{},"never"," 是一个看起来和他俩没关系，但实际上又有点关系的东西。",[81,1399,1396],{}," 是实质上的底层类型，也就是说，",[62,1402,1403,1404,1406,1407,1409],{},"啥都不是 ",[81,1405,1396],{},"， ",[81,1408,1396],{}," 啥都是","。 通常在 TS 里面， ",[81,1412,1396],{}," 表示一个不可能出现的类型。比方说：",[74,1415,1417],{"className":76,"code":1416,"language":78,"meta":79,"style":79},"type A = true & false \u002F\u002F never\n\nfunction foo(): never { \u002F\u002F no error\n  throw new Error()\n}\n",[81,1418,1419,1437,1441,1459,1474],{"__ignoreMap":79},[84,1420,1421,1423,1425,1427,1429,1431,1434],{"class":86,"line":87},[84,1422,630],{"class":97},[84,1424,886],{"class":199},[84,1426,139],{"class":105},[84,1428,1037],{"class":199},[84,1430,720],{"class":105},[84,1432,1433],{"class":199}," false",[84,1435,1436],{"class":90}," \u002F\u002F never\n",[84,1438,1439],{"class":86,"line":94},[84,1440,179],{"emptyLinePlaceholder":178},[84,1442,1443,1445,1448,1451,1454,1456],{"class":86,"line":116},[84,1444,119],{"class":97},[84,1446,1447],{"class":122}," foo",[84,1449,1450],{"class":105},"():",[84,1452,1453],{"class":199}," never",[84,1455,723],{"class":105},[84,1457,1458],{"class":90}," \u002F\u002F no error\n",[84,1460,1461,1465,1468,1471],{"class":86,"line":151},[84,1462,1464],{"class":1463},"s7zQu","  throw",[84,1466,1467],{"class":105}," new",[84,1469,1470],{"class":122}," Error",[84,1472,1473],{"class":491},"()\n",[84,1475,1476],{"class":86,"line":169},[84,1477,172],{"class":105},[19,1479,1480,1481],{},"所以，如果你期待一个分支逻辑永远不可能走到，那么就可以为这个分支的变量、类型或者是返回值声明类型 ",[81,1482,1396],{},[377,1484,1485],{},[19,1486,1487,1488,1490,1491,1493,1494,1496,1497],{},"从集合论的角度来看，",[81,1489,1396],{}," 实际上是类型领域的 ∅，而 ",[81,1492,1262],{}," 是全集。",[81,1495,1107],{}," 某种程度上可以理解为所谓的“全部集合构成的集合”，也就是罗素所指的极限类。在 ZF 公理系统中，由于正则公理的存在，这样的东西并不是一个集合，因为它涉及一个无法良定义的自指。这也可以强行解释为啥不要用 ",[81,1498,1107],{},[19,1500,1501,1502,1505,1506,1508,1509,1511,1512,1515],{},"另外还有一个内置类型是 ",[81,1503,1504],{},"void","。这个类型曾经是用来解决没有 ",[81,1507,1262],{}," 时的一系列问题的，简单地说，",[81,1510,1504],{}," 作为函数类型的返回值时是顶层类型，作为独立类型则等价于 ",[81,1513,1514],{},"undefined","。也就是说",[74,1517,1519],{"className":76,"code":1518,"language":78,"meta":79,"style":79},"type A = () => void\n\nconst a: A = () => true \u002F\u002F no error\n\nfunction foo(): void {\n  return 1 \u002F\u002F error\n}\n\nfunction bar(): void {\n  return undefined \u002F\u002F no error\n}\n",[81,1520,1521,1536,1540,1562,1566,1578,1588,1592,1596,1609,1618],{"__ignoreMap":79},[84,1522,1523,1525,1527,1529,1532,1534],{"class":86,"line":87},[84,1524,630],{"class":97},[84,1526,886],{"class":199},[84,1528,139],{"class":105},[84,1530,1531],{"class":105}," ()",[84,1533,715],{"class":97},[84,1535,329],{"class":199},[84,1537,1538],{"class":86,"line":94},[84,1539,179],{"emptyLinePlaceholder":178},[84,1541,1542,1544,1547,1549,1551,1553,1555,1557,1560],{"class":86,"line":116},[84,1543,98],{"class":97},[84,1545,1546],{"class":101}," a",[84,1548,196],{"class":105},[84,1550,886],{"class":199},[84,1552,139],{"class":105},[84,1554,1531],{"class":105},[84,1556,715],{"class":97},[84,1558,1037],{"class":1559},"sfNiH",[84,1561,1458],{"class":90},[84,1563,1564],{"class":86,"line":151},[84,1565,179],{"emptyLinePlaceholder":178},[84,1567,1568,1570,1572,1574,1576],{"class":86,"line":169},[84,1569,119],{"class":97},[84,1571,1447],{"class":122},[84,1573,1450],{"class":105},[84,1575,239],{"class":199},[84,1577,148],{"class":105},[84,1579,1580,1583,1585],{"class":86,"line":175},[84,1581,1582],{"class":1463},"  return",[84,1584,110],{"class":109},[84,1586,1587],{"class":90}," \u002F\u002F error\n",[84,1589,1590],{"class":86,"line":182},[84,1591,172],{"class":105},[84,1593,1594],{"class":86,"line":188},[84,1595,179],{"emptyLinePlaceholder":178},[84,1597,1598,1600,1603,1605,1607],{"class":86,"line":209},[84,1599,119],{"class":97},[84,1601,1602],{"class":122}," bar",[84,1604,1450],{"class":105},[84,1606,239],{"class":199},[84,1608,148],{"class":105},[84,1610,1611,1613,1616],{"class":86,"line":244},[84,1612,1582],{"class":1463},[84,1614,1615],{"class":105}," undefined",[84,1617,1458],{"class":90},[84,1619,1620],{"class":86,"line":259},[84,1621,172],{"class":105},[19,1623,1624,1625,1627,1628,1630,1631,1633],{},"目前来说应当尽量避免使用 ",[81,1626,1504],{}," 类型，通常来说应该使用 ",[81,1629,1514],{},"；而对于二者不同的情况，使用 ",[81,1632,1262],{}," 是更加安全的选择。",[383,1635,1637],{"id":1636},"operations-and-type-parameters","类型运算和类型参数",[19,1639,1640],{},"在上面的例子里你可能看到过了，类型支持一种像是位运算的东西，就是与或操作符：",[74,1642,1644],{"className":76,"code":1643,"language":78,"meta":79,"style":79},"type A = string | number \u002F\u002F 要么是 string，要么是 number\ntype B = A & (number | symbol) \u002F\u002F 现在 B 只能是 number 了\n",[81,1645,1646,1663],{"__ignoreMap":79},[84,1647,1648,1650,1652,1654,1656,1658,1660],{"class":86,"line":87},[84,1649,630],{"class":97},[84,1651,886],{"class":199},[84,1653,139],{"class":105},[84,1655,497],{"class":199},[84,1657,976],{"class":105},[84,1659,200],{"class":199},[84,1661,1662],{"class":90}," \u002F\u002F 要么是 string，要么是 number\n",[84,1664,1665,1667,1669,1671,1673,1675,1677,1680,1682,1685,1688],{"class":86,"line":94},[84,1666,630],{"class":97},[84,1668,913],{"class":199},[84,1670,139],{"class":105},[84,1672,886],{"class":199},[84,1674,720],{"class":105},[84,1676,696],{"class":101},[84,1678,1679],{"class":199},"number",[84,1681,976],{"class":105},[84,1683,1684],{"class":199}," symbol",[84,1686,1687],{"class":101},") ",[84,1689,1690],{"class":90},"\u002F\u002F 现在 B 只能是 number 了\n",[377,1692,1693],{},[19,1694,1695,1696,1698],{},"注意类型是没有非这个操作的，如果要实现这个效果就要用到 ",[81,1697,866],{}," 或者是内置工具类型了",[19,1700,1701],{},"除了这俩操作符之外，你还可以用这些东西：",[39,1703,1704],{},[42,1705,1706],{},"下标操作符",[74,1708,1710],{"className":76,"code":1709,"language":78,"meta":79,"style":79},"type A = { name: string }\ntype B = A['name'] \u002F\u002F string\n",[81,1711,1712,1732],{"__ignoreMap":79},[84,1713,1714,1716,1718,1720,1722,1725,1727,1729],{"class":86,"line":87},[84,1715,630],{"class":97},[84,1717,886],{"class":199},[84,1719,139],{"class":105},[84,1721,723],{"class":105},[84,1723,1724],{"class":491}," name",[84,1726,196],{"class":105},[84,1728,497],{"class":199},[84,1730,1731],{"class":105}," }\n",[84,1733,1734,1736,1738,1740,1742,1745,1747,1750,1752,1754],{"class":86,"line":94},[84,1735,630],{"class":97},[84,1737,913],{"class":199},[84,1739,139],{"class":105},[84,1741,886],{"class":199},[84,1743,1744],{"class":101},"[",[84,1746,1058],{"class":105},[84,1748,1749],{"class":1054},"name",[84,1751,1058],{"class":105},[84,1753,1083],{"class":101},[84,1755,1756],{"class":90},"\u002F\u002F string\n",[19,1758,1759,1760,1763],{},"注意不能写成 ",[81,1761,1762],{},"A.name"," 哦！",[39,1765,1766],{},[42,1767,1768,1771],{},[81,1769,1770],{},"keyof"," 操作符",[74,1773,1775],{"className":76,"code":1774,"language":78,"meta":79,"style":79},"type A = { name: string }\ntype B = keyof A \u002F\u002F 'name'\n",[81,1776,1777,1795],{"__ignoreMap":79},[84,1778,1779,1781,1783,1785,1787,1789,1791,1793],{"class":86,"line":87},[84,1780,630],{"class":97},[84,1782,886],{"class":199},[84,1784,139],{"class":105},[84,1786,723],{"class":105},[84,1788,1724],{"class":491},[84,1790,196],{"class":105},[84,1792,497],{"class":199},[84,1794,1731],{"class":105},[84,1796,1797,1799,1801,1803,1806,1808],{"class":86,"line":94},[84,1798,630],{"class":97},[84,1800,913],{"class":199},[84,1802,139],{"class":105},[84,1804,1805],{"class":105}," keyof",[84,1807,886],{"class":199},[84,1809,1810],{"class":90}," \u002F\u002F 'name'\n",[19,1812,1813,1814,1817],{},"有聪明的小朋友会问了，那有没有 ",[81,1815,1816],{},"valueof"," 呀？",[19,1819,1820],{},"你看看这样行不：",[74,1822,1827],{"className":1823,"code":1825,"language":1826},[1824],"language-text","type A = { name: string }\ntype B = A[keyof A] \u002F\u002F string\n","text",[81,1828,1825],{"__ignoreMap":79},[19,1830,1831],{},"你觉得不好用？那等会儿你再看看（",[39,1833,1834],{},[42,1835,1836,1771],{},[81,1837,1838],{},"typeof",[74,1840,1842],{"className":76,"code":1841,"language":78,"meta":79,"style":79},"let el: HTMLElement\ntype A = typeof el \u002F\u002F HTMLElement\n",[81,1843,1844,1856],{"__ignoreMap":79},[84,1845,1846,1848,1851,1853],{"class":86,"line":87},[84,1847,1159],{"class":97},[84,1849,1850],{"class":101}," el",[84,1852,196],{"class":105},[84,1854,1855],{"class":199}," HTMLElement\n",[84,1857,1858,1860,1862,1864,1867,1870],{"class":86,"line":94},[84,1859,630],{"class":97},[84,1861,886],{"class":199},[84,1863,139],{"class":105},[84,1865,1866],{"class":105}," typeof",[84,1868,1869],{"class":101}," el ",[84,1871,1872],{"class":90},"\u002F\u002F HTMLElement\n",[19,1874,1875],{},"（其实还有一些其他的关键字操作符，但实际使用的场景极少）",[19,1877,1878],{},"然后你发现，哦豁，这样的话类型有点像一种变量似的，可以声明，又可以运算，就差整一个给类型用的函数了。",[19,1880,1881],{},"这个真有！如果你写过 C++ 的话，这就和传说中的模板一样：",[74,1883,1885],{"className":76,"code":1884,"language":78,"meta":79,"style":79},"type A\u003CX, Y> = X | { name: Y }\n\ntype B = A\u003Cstring, number> \u002F\u002F string | { name: number }\n",[81,1886,1887,1924,1928],{"__ignoreMap":79},[84,1888,1889,1891,1893,1896,1899,1901,1904,1907,1909,1912,1914,1916,1918,1920,1922],{"class":86,"line":87},[84,1890,630],{"class":97},[84,1892,886],{"class":199},[84,1894,1895],{"class":105},"\u003C",[84,1897,1898],{"class":199},"X",[84,1900,133],{"class":105},[84,1902,1903],{"class":199}," Y",[84,1905,1906],{"class":105},">",[84,1908,139],{"class":105},[84,1910,1911],{"class":199}," X",[84,1913,976],{"class":105},[84,1915,723],{"class":105},[84,1917,1724],{"class":491},[84,1919,196],{"class":105},[84,1921,1903],{"class":199},[84,1923,1731],{"class":105},[84,1925,1926],{"class":86,"line":94},[84,1927,179],{"emptyLinePlaceholder":178},[84,1929,1930,1932,1934,1936,1938,1940,1942,1944,1946,1948],{"class":86,"line":116},[84,1931,630],{"class":97},[84,1933,913],{"class":199},[84,1935,139],{"class":105},[84,1937,886],{"class":199},[84,1939,1895],{"class":105},[84,1941,1076],{"class":199},[84,1943,133],{"class":105},[84,1945,200],{"class":199},[84,1947,1906],{"class":105},[84,1949,1950],{"class":90}," \u002F\u002F string | { name: number }\n",[19,1952,1953],{},"你甚至可以写参数的默认值",[74,1955,1957],{"className":76,"code":1956,"language":78,"meta":79,"style":79},"type K\u003CValue, Key = string> = Value[Key]\n",[81,1958,1959],{"__ignoreMap":79},[84,1960,1961,1963,1966,1968,1971,1973,1976,1978,1980,1982,1984,1987,1989,1992],{"class":86,"line":87},[84,1962,630],{"class":97},[84,1964,1965],{"class":199}," K",[84,1967,1895],{"class":105},[84,1969,1970],{"class":199},"Value",[84,1972,133],{"class":105},[84,1974,1975],{"class":199}," Key",[84,1977,139],{"class":105},[84,1979,497],{"class":199},[84,1981,1906],{"class":105},[84,1983,139],{"class":105},[84,1985,1986],{"class":199}," Value",[84,1988,1744],{"class":101},[84,1990,1991],{"class":199},"Key",[84,1993,1994],{"class":101},"]\n",[19,1996,1997],{},"甚至是“类型的类型”（使用 extends 限定类型参数的类型）",[74,1999,2001],{"className":76,"code":2000,"language":78,"meta":79,"style":79},"type K\u003CValue, Key extends keyof Value> = Value[Key]\n",[81,2002,2003],{"__ignoreMap":79},[84,2004,2005,2007,2009,2011,2013,2015,2017,2019,2021,2023,2025,2027,2029,2031,2033],{"class":86,"line":87},[84,2006,630],{"class":97},[84,2008,1965],{"class":199},[84,2010,1895],{"class":105},[84,2012,1970],{"class":199},[84,2014,133],{"class":105},[84,2016,1975],{"class":199},[84,2018,916],{"class":97},[84,2020,1805],{"class":105},[84,2022,1986],{"class":199},[84,2024,1906],{"class":105},[84,2026,139],{"class":105},[84,2028,1986],{"class":199},[84,2030,1744],{"class":101},[84,2032,1991],{"class":199},[84,2034,1994],{"class":101},[19,2036,2037,2038,2040],{},"于是刚才说的 ",[81,2039,1816],{}," 终于有了着落：",[74,2042,2044],{"className":76,"code":2043,"language":78,"meta":79,"style":79},"type ValueOf\u003CT> = T[keyof T]\n",[81,2045,2046],{"__ignoreMap":79},[84,2047,2048,2050,2053,2055,2058,2060,2062,2065,2067,2069,2071],{"class":86,"line":87},[84,2049,630],{"class":97},[84,2051,2052],{"class":199}," ValueOf",[84,2054,1895],{"class":105},[84,2056,2057],{"class":199},"T",[84,2059,1906],{"class":105},[84,2061,139],{"class":105},[84,2063,2064],{"class":199}," T",[84,2066,1744],{"class":101},[84,2068,1770],{"class":105},[84,2070,2064],{"class":199},[84,2072,1994],{"class":101},[383,2074,2076],{"id":2075},"access-modifiers","访问修饰符",[19,2078,2079,2080,2083],{},"在 TypeScript 世界里你可以声明一些仅限于编译期的限制，例如 ",[81,2081,2082],{},"private","：",[74,2085,2087],{"className":76,"code":2086,"language":78,"meta":79,"style":79},"class Foo {\n  private name: string;\n}\n",[81,2088,2089,2098,2111],{"__ignoreMap":79},[84,2090,2091,2093,2096],{"class":86,"line":87},[84,2092,838],{"class":97},[84,2094,2095],{"class":199}," Foo",[84,2097,148],{"class":105},[84,2099,2100,2103,2105,2107,2109],{"class":86,"line":94},[84,2101,2102],{"class":97},"  private",[84,2104,1724],{"class":491},[84,2106,196],{"class":105},[84,2108,497],{"class":199},[84,2110,113],{"class":105},[84,2112,2113],{"class":86,"line":116},[84,2114,172],{"class":105},[19,2116,2117,2118,2121,2122,2124,2125,2128,2129,2132],{},"这样外部就不能访问 ",[81,2119,2120],{},"Foo"," 实例的 ",[81,2123,1749],{}," 属性了。同理也可以使用 ",[81,2126,2127],{},"protected"," 和 ",[81,2130,2131],{},"public","。",[19,2134,2135,2136,2083],{},"除了这些之外还有一个很常用的是 ",[81,2137,2138],{},"readonly",[74,2140,2142],{"className":76,"code":2141,"language":78,"meta":79,"style":79},"interface Bar {\n  readonly id: string;\n}\n",[81,2143,2144,2153,2167],{"__ignoreMap":79},[84,2145,2146,2148,2151],{"class":86,"line":87},[84,2147,471],{"class":97},[84,2149,2150],{"class":199}," Bar",[84,2152,148],{"class":105},[84,2154,2155,2158,2161,2163,2165],{"class":86,"line":94},[84,2156,2157],{"class":97},"  readonly",[84,2159,2160],{"class":491}," id",[84,2162,196],{"class":105},[84,2164,497],{"class":199},[84,2166,113],{"class":105},[84,2168,2169],{"class":86,"line":116},[84,2170,172],{"class":105},[19,2172,2173,2174,2177,2178,2181],{},"这样就可以表示 ",[81,2175,2176],{},"Bar"," 类型的对象的 ",[81,2179,2180],{},"id"," 是不能被修改的",[14,2183,2185],{"id":2184},"type-matching","类型匹配",[19,2187,2188],{},"有没有思考过一个问题，TypeScript 和 Java 的类型系统有哪里不一样？如果你够敏感的话，从上面的例子可能会发现一些端倪。",[19,2190,2191],{},"假设我给我的函数声明了一个类型：",[74,2193,2195],{"className":76,"code":2194,"language":78,"meta":79,"style":79},"interface MyInterface {\n  name: string\n}\n\nfunction printName(value: MyInterface) {\n  console.log(value.name)\n}\n",[81,2196,2197,2206,2214,2218,2222,2241,2263],{"__ignoreMap":79},[84,2198,2199,2201,2204],{"class":86,"line":87},[84,2200,471],{"class":97},[84,2202,2203],{"class":199}," MyInterface",[84,2205,148],{"class":105},[84,2207,2208,2210,2212],{"class":86,"line":94},[84,2209,504],{"class":491},[84,2211,196],{"class":105},[84,2213,958],{"class":199},[84,2215,2216],{"class":86,"line":116},[84,2217,172],{"class":105},[84,2219,2220],{"class":86,"line":151},[84,2221,179],{"emptyLinePlaceholder":178},[84,2223,2224,2226,2229,2231,2233,2235,2237,2239],{"class":86,"line":169},[84,2225,119],{"class":97},[84,2227,2228],{"class":122}," printName",[84,2230,126],{"class":105},[84,2232,130],{"class":129},[84,2234,196],{"class":105},[84,2236,2203],{"class":199},[84,2238,145],{"class":105},[84,2240,148],{"class":105},[84,2242,2243,2246,2249,2252,2254,2256,2258,2260],{"class":86,"line":175},[84,2244,2245],{"class":101},"  console",[84,2247,2248],{"class":105},".",[84,2250,2251],{"class":122},"log",[84,2253,126],{"class":491},[84,2255,130],{"class":101},[84,2257,2248],{"class":105},[84,2259,1749],{"class":101},[84,2261,2262],{"class":491},")\n",[84,2264,2265],{"class":86,"line":182},[84,2266,172],{"class":105},[19,2268,2269],{},"然后又调用了这个函数",[74,2271,2273],{"className":76,"code":2272,"language":78,"meta":79,"style":79},"printName({ name: '123' })\n",[81,2274,2275],{"__ignoreMap":79},[84,2276,2277,2280,2282,2285,2287,2289,2291,2294,2296,2299],{"class":86,"line":87},[84,2278,2279],{"class":122},"printName",[84,2281,126],{"class":101},[84,2283,2284],{"class":105},"{",[84,2286,1724],{"class":491},[84,2288,196],{"class":105},[84,2290,1051],{"class":105},[84,2292,2293],{"class":1054},"123",[84,2295,1058],{"class":105},[84,2297,2298],{"class":105}," }",[84,2300,2262],{"class":101},[19,2302,2303],{},"你会发现，这段代码！！竟然！！运行的好好的！！！！",[19,2305,2306,2307,2310,2311,2314,2315,2317,2318,2317,2321,2323,2324,2326],{},"你可能觉得有点大惊小怪，但是仔细想一想，你的字面量 ",[81,2308,2309],{},"{ name: '123' }"," 并没有被声明成 ",[81,2312,2313],{},"MyInterface"," 类型的，而 TypeScript 也无从推导。但实际上，",[81,2316,2309],{}," ",[62,2319,2320],{},"就像是",[81,2322,2313],{}," —— 因为从某种角度评估来说他们的类型就是能够匹配的，这个角度就是 TypeScript 的",[62,2325,2185],{},"。在主流的静态类型语言中，你是不可能遇到这种情况的，而这就是 TypeScript 能够适合前端开发的重要原因之一。",[19,2328,2329,2330,2333,2334,2336],{},"也正因为如此，在这个例子里，你可以把 ",[81,2331,2332],{},"{ name: '123', value: 123 }"," 传给 ",[81,2335,2279],{}," —— TypeScript 并不会因为你多一个字段而拒绝你，因为这个类型依然是能够匹配的。",[14,2338,2340],{"id":2339},"type-assertions","强制类型声明",[19,2342,2343,2344,2346,2347,2349,2350,2352],{},"到现在为止，你已经成为一个拥有 TypeScript 开发能力的前端了！作为一个优秀的 TypeScript 开发者，你总是谨记上面说的，不用 ",[81,2345,1107],{}," 不用 ",[81,2348,1107],{},"，也不用 ",[81,2351,1262],{},"。终于有一天，你遇到了一个自己写不出来的代码：",[74,2354,2356],{"className":76,"code":2355,"language":78,"meta":79,"style":79},"function readFile(file: File, callback: (value: string) => unknown) {\n  const reader = new FileReader();\n  reader.readAsDataURL(file);\n  reader.addEventListener('load', () => callback(reader.result)); \u002F\u002F error!\n}\n",[81,2357,2358,2400,2420,2438,2483],{"__ignoreMap":79},[84,2359,2360,2362,2365,2367,2370,2372,2375,2377,2380,2382,2384,2386,2388,2390,2392,2394,2396,2398],{"class":86,"line":87},[84,2361,119],{"class":97},[84,2363,2364],{"class":122}," readFile",[84,2366,126],{"class":105},[84,2368,2369],{"class":129},"file",[84,2371,196],{"class":105},[84,2373,2374],{"class":199}," File",[84,2376,133],{"class":105},[84,2378,2379],{"class":122}," callback",[84,2381,196],{"class":105},[84,2383,696],{"class":105},[84,2385,130],{"class":129},[84,2387,196],{"class":105},[84,2389,497],{"class":199},[84,2391,145],{"class":105},[84,2393,715],{"class":97},[84,2395,1306],{"class":199},[84,2397,145],{"class":105},[84,2399,148],{"class":105},[84,2401,2402,2405,2408,2410,2412,2415,2418],{"class":86,"line":94},[84,2403,2404],{"class":97},"  const",[84,2406,2407],{"class":101}," reader",[84,2409,139],{"class":105},[84,2411,1467],{"class":105},[84,2413,2414],{"class":122}," FileReader",[84,2416,2417],{"class":491},"()",[84,2419,113],{"class":105},[84,2421,2422,2425,2427,2430,2432,2434,2436],{"class":86,"line":116},[84,2423,2424],{"class":101},"  reader",[84,2426,2248],{"class":105},[84,2428,2429],{"class":122},"readAsDataURL",[84,2431,126],{"class":491},[84,2433,2369],{"class":101},[84,2435,145],{"class":491},[84,2437,113],{"class":105},[84,2439,2440,2442,2444,2447,2449,2451,2454,2456,2458,2460,2462,2464,2466,2469,2471,2474,2477,2480],{"class":86,"line":151},[84,2441,2424],{"class":101},[84,2443,2248],{"class":105},[84,2445,2446],{"class":122},"addEventListener",[84,2448,126],{"class":491},[84,2450,1058],{"class":105},[84,2452,2453],{"class":1054},"load",[84,2455,1058],{"class":105},[84,2457,133],{"class":105},[84,2459,1531],{"class":105},[84,2461,715],{"class":97},[84,2463,2379],{"class":122},[84,2465,126],{"class":491},[84,2467,2468],{"class":101},"reader",[84,2470,2248],{"class":105},[84,2472,2473],{"class":101},"result",[84,2475,2476],{"class":491},"))",[84,2478,2479],{"class":105},";",[84,2481,2482],{"class":90}," \u002F\u002F error!\n",[84,2484,2485],{"class":86,"line":169},[84,2486,172],{"class":105},[19,2488,2489,2490,2493,2494,2497,2498,2500],{},"你发现，",[81,2491,2492],{},"reader.result"," 的类型是 ",[81,2495,2496],{},"string | Buffer | null","，可是你很委屈，文档上说这里的 ",[81,2499,2473],{}," 就是字符串呀！",[19,2502,2503,2504,2507,2508,2513,2514,2516,2517,2520],{},"显然不是文档写错了，也不是 TypeScript 内置的 DOM 类型有问题。因为 ",[81,2505,2506],{},"FileReader"," 这个类还有一个 ",[23,2509,2512],{"href":2510,"rel":2511},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fzh-CN\u002Fdocs\u002FWeb\u002FAPI\u002FFileReader\u002FreadAsArrayBuffer",[27],"readAsArrayBuffer"," 的方法。在这里，",[81,2515,2473],{}," 显然不能提前预知你到底调用的是 ",[81,2518,2519],{},"readAs"," 啥。那这个时候应该怎么办呢？",[19,2522,2523,2524,2527,2528,2530],{},"很简单，你可以告诉 TypeScript，",[62,2525,2526],{},"我不要你觉得，我要我觉得","！这里的类型就是 ",[81,2529,1076],{},"！这个语法有点像类型参数：",[74,2532,2534],{"className":76,"code":2533,"language":78,"meta":79,"style":79},"callback(\u003Cstring>reader.result)\n",[81,2535,2536],{"__ignoreMap":79},[84,2537,2538,2541,2544,2546,2549,2551],{"class":86,"line":87},[84,2539,2540],{"class":122},"callback",[84,2542,2543],{"class":101},"(\u003C",[84,2545,1076],{"class":199},[84,2547,2548],{"class":101},">reader",[84,2550,2248],{"class":105},[84,2552,2553],{"class":101},"result)\n",[19,2555,2556,2557,2559],{},"这个操作就叫",[62,2558,2340],{},"。注意这个尖括号的优先级是很低的，有时候你可能需要加括号来使用：",[74,2561,2563],{"className":76,"code":2562,"language":78,"meta":79,"style":79},"(\u003Cstring>reader.result).length\n",[81,2564,2565],{"__ignoreMap":79},[84,2566,2567,2569,2571,2573,2575,2578,2580],{"class":86,"line":87},[84,2568,2543],{"class":101},[84,2570,1076],{"class":199},[84,2572,2548],{"class":101},[84,2574,2248],{"class":105},[84,2576,2577],{"class":101},"result)",[84,2579,2248],{"class":105},[84,2581,2582],{"class":101},"length\n",[19,2584,2585],{},"OK，现在一切都没有问题了对不对？并不！如果你使用 React，你就会发现，这个语法好像很熟悉！",[74,2587,2589],{"className":76,"code":2588,"language":78,"meta":79,"style":79},"type div = {\n  name: string\n}\n\n\nconsole.log(\u003Cdiv>{name: 2})\n",[81,2590,2591,2602,2610,2614,2618,2622],{"__ignoreMap":79},[84,2592,2593,2595,2598,2600],{"class":86,"line":87},[84,2594,630],{"class":97},[84,2596,2597],{"class":199}," div",[84,2599,139],{"class":105},[84,2601,148],{"class":105},[84,2603,2604,2606,2608],{"class":86,"line":94},[84,2605,504],{"class":491},[84,2607,196],{"class":105},[84,2609,958],{"class":199},[84,2611,2612],{"class":86,"line":116},[84,2613,172],{"class":105},[84,2615,2616],{"class":86,"line":151},[84,2617,179],{"emptyLinePlaceholder":178},[84,2619,2620],{"class":86,"line":169},[84,2621,179],{"emptyLinePlaceholder":178},[84,2623,2624,2627,2629,2631,2633,2635,2638,2641,2643,2645,2648,2651],{"class":86,"line":175},[84,2625,2626],{"class":199},"console",[84,2628,2248],{"class":105},[84,2630,2251],{"class":199},[84,2632,126],{"class":101},[84,2634,1895],{"class":105},[84,2636,2637],{"class":199},"div",[84,2639,2640],{"class":105},">{",[84,2642,1749],{"class":491},[84,2644,196],{"class":105},[84,2646,2647],{"class":109}," 2",[84,2649,2650],{"class":105},"}",[84,2652,2262],{"class":101},[19,2654,2655],{},"对于转译器来说，它不能理解你这里的 ",[2637,2657,2658,2659,2662,2663,2686,2695,2705,2753,2756,2884,2891,2895,2902,2914,2925,3045,3055,3077,3081,3084,3130,3133,3144,3193,3196,3202,3205,3329,3344,3373,3404,3407,3411,3414,3430,3468,3471,3480,3540,3544,3547,3584,3598,3611,3659,3662,3667,3710,3718,3721,3727,3733,3767,3776,3782,3826,3832,3893,3904,3909,3960,3975,3979,3982,4095,4164,4197,4261,4282,4354,4374,4437,4452,4498,4512,4556,4571,4599],{}," 究竟是 JSX 标签还是强制类型声明。为了解决这个问题，TypeScript 引入了 ",[81,2660,2661],{},"as"," 操作符，你可以写成",[74,2664,2666],{"className":76,"code":2665,"language":78,"meta":79,"style":79},"callback(reader.result as string)\n",[81,2667,2668],{"__ignoreMap":79},[84,2669,2670,2672,2675,2677,2680,2682,2684],{"class":86,"line":87},[84,2671,2540],{"class":122},[84,2673,2674],{"class":101},"(reader",[84,2676,2248],{"class":105},[84,2678,2679],{"class":101},"result ",[84,2681,2661],{"class":1463},[84,2683,497],{"class":199},[84,2685,2262],{"class":101},[19,2687,2688,2689,2691,2692,2694],{},"通常来说我们提倡使用 ",[81,2690,2661],{}," 来避免语法冲突。同时，",[81,2693,2661],{}," 也具有相当强的语义，你可以一眼看出来这里有一个强制类型转换操作。",[19,2696,2697,2698,2700,2701,2704],{},"当使用 ",[81,2699,2661],{}," 时也有一个特殊的 ",[81,2702,2703],{},"as const"," 的用法，对于类型推导很有帮助",[74,2706,2708],{"className":76,"code":2707,"language":78,"meta":79,"style":79},"let foo = 'abc' \u002F\u002F foo 的类型为 string\nlet bar = 'abc' as const \u002F\u002F bar 的类型为 'abc'\n",[81,2709,2710,2729],{"__ignoreMap":79},[84,2711,2712,2714,2717,2719,2721,2724,2726],{"class":86,"line":87},[84,2713,1159],{"class":97},[84,2715,2716],{"class":101}," foo ",[84,2718,106],{"class":105},[84,2720,1051],{"class":105},[84,2722,2723],{"class":1054},"abc",[84,2725,1058],{"class":105},[84,2727,2728],{"class":90}," \u002F\u002F foo 的类型为 string\n",[84,2730,2731,2733,2736,2738,2740,2742,2744,2747,2750],{"class":86,"line":94},[84,2732,1159],{"class":97},[84,2734,2735],{"class":101}," bar ",[84,2737,106],{"class":105},[84,2739,1051],{"class":105},[84,2741,2723],{"class":1054},[84,2743,1058],{"class":105},[84,2745,2746],{"class":1463}," as",[84,2748,2749],{"class":199}," const",[84,2751,2752],{"class":90}," \u002F\u002F bar 的类型为 'abc'\n",[19,2754,2755],{},"很多时候我们既可以使用类型参数又可以使用强制类型声明。例如：",[74,2757,2759],{"className":76,"code":2758,"language":78,"meta":79,"style":79},"const a = [videoInfo, userInfo].reduce\u003CCommonInfo>((prev, current) => Object.assign(prev, current)), {})\n\nconst a = [videoInfo, userInfo].reduce((prev, current) => Object.assign(prev, current)), {} as CommonInfo)\n",[81,2760,2761,2829,2833],{"__ignoreMap":79},[84,2762,2763,2765,2768,2770,2773,2775,2778,2780,2783,2785,2788,2790,2792,2794,2797,2799,2802,2804,2806,2809,2811,2814,2817,2819,2822,2824,2827],{"class":86,"line":87},[84,2764,98],{"class":97},[84,2766,2767],{"class":101}," a ",[84,2769,106],{"class":105},[84,2771,2772],{"class":101}," [videoInfo",[84,2774,133],{"class":105},[84,2776,2777],{"class":101}," userInfo]",[84,2779,2248],{"class":105},[84,2781,2782],{"class":122},"reduce",[84,2784,1895],{"class":105},[84,2786,2787],{"class":199},"CommonInfo",[84,2789,1906],{"class":105},[84,2791,126],{"class":101},[84,2793,126],{"class":105},[84,2795,2796],{"class":129},"prev",[84,2798,133],{"class":105},[84,2800,2801],{"class":129}," current",[84,2803,145],{"class":105},[84,2805,715],{"class":97},[84,2807,2808],{"class":101}," Object",[84,2810,2248],{"class":105},[84,2812,2813],{"class":122},"assign",[84,2815,2816],{"class":101},"(prev",[84,2818,133],{"class":105},[84,2820,2821],{"class":101}," current))",[84,2823,133],{"class":105},[84,2825,2826],{"class":105}," {}",[84,2828,2262],{"class":101},[84,2830,2831],{"class":86,"line":94},[84,2832,179],{"emptyLinePlaceholder":178},[84,2834,2835,2837,2839,2841,2843,2845,2847,2849,2851,2853,2855,2857,2859,2861,2863,2865,2867,2869,2871,2873,2875,2877,2879,2881],{"class":86,"line":116},[84,2836,98],{"class":97},[84,2838,2767],{"class":101},[84,2840,106],{"class":105},[84,2842,2772],{"class":101},[84,2844,133],{"class":105},[84,2846,2777],{"class":101},[84,2848,2248],{"class":105},[84,2850,2782],{"class":122},[84,2852,126],{"class":101},[84,2854,126],{"class":105},[84,2856,2796],{"class":129},[84,2858,133],{"class":105},[84,2860,2801],{"class":129},[84,2862,145],{"class":105},[84,2864,715],{"class":97},[84,2866,2808],{"class":101},[84,2868,2248],{"class":105},[84,2870,2813],{"class":122},[84,2872,2816],{"class":101},[84,2874,133],{"class":105},[84,2876,2821],{"class":101},[84,2878,133],{"class":105},[84,2880,2826],{"class":105},[84,2882,2883],{"class":101}," as CommonInfo)\n",[19,2885,2886,2887,2890],{},"这两种写法都是可以正常工作的。通常来说，",[62,2888,2889],{},"能够使用类型参数的情况就可以不使用强制类型声明","。在一些复杂的重载情况中，这会提高类型检查的性能。",[14,2892,2894],{"id":2893},"functions","函数",[383,2896,2898,2901],{"id":2897},"this-type",[81,2899,2900],{},"this"," 类型",[19,2903,2904,2905,2907,2908,2910,2911,2913],{},"JavaScript 有一个让新手头疼的特性，就是函数的 ",[81,2906,2900],{}," 绑定。一个函数会在不同的使用场合出现不同的 ",[81,2909,2900],{},"，这就意味着 ",[81,2912,2900],{}," 的类型是无从推导的。",[19,2915,2916,2917,2919,2920,2922,2923],{},"但你仍然可以手动声明 ",[81,2918,2900],{}," 的类型，以避免 TypeScript 把你的 ",[81,2921,2900],{}," 当做 ",[81,2924,1107],{},[74,2926,2928],{"className":76,"code":2927,"language":78,"meta":79,"style":79},"function get(this: Array, index: number) {\n  return this[index >= 0 ? index : this.length + index]\n}\n\nget.call([1, 2, 3], -1) \u002F\u002F 3\n",[81,2929,2930,2959,2997,3001,3005],{"__ignoreMap":79},[84,2931,2932,2934,2937,2939,2941,2943,2946,2948,2951,2953,2955,2957],{"class":86,"line":87},[84,2933,119],{"class":97},[84,2935,2936],{"class":122}," get",[84,2938,126],{"class":105},[84,2940,2900],{"class":1463},[84,2942,196],{"class":105},[84,2944,2945],{"class":199}," Array",[84,2947,133],{"class":105},[84,2949,2950],{"class":129}," index",[84,2952,196],{"class":105},[84,2954,200],{"class":199},[84,2956,145],{"class":105},[84,2958,148],{"class":105},[84,2960,2961,2963,2966,2968,2971,2974,2976,2979,2981,2984,2987,2990,2993,2995],{"class":86,"line":94},[84,2962,1582],{"class":1463},[84,2964,2965],{"class":105}," this",[84,2967,1744],{"class":491},[84,2969,2970],{"class":101},"index",[84,2972,2973],{"class":105}," >=",[84,2975,142],{"class":109},[84,2977,2978],{"class":105}," ?",[84,2980,2950],{"class":101},[84,2982,2983],{"class":105}," :",[84,2985,2986],{"class":105}," this.",[84,2988,2989],{"class":101},"length",[84,2991,2992],{"class":105}," +",[84,2994,2950],{"class":101},[84,2996,1994],{"class":491},[84,2998,2999],{"class":86,"line":116},[84,3000,172],{"class":105},[84,3002,3003],{"class":86,"line":151},[84,3004,179],{"emptyLinePlaceholder":178},[84,3006,3007,3010,3012,3015,3018,3021,3023,3025,3027,3030,3033,3035,3038,3040,3042],{"class":86,"line":169},[84,3008,3009],{"class":101},"get",[84,3011,2248],{"class":105},[84,3013,3014],{"class":122},"call",[84,3016,3017],{"class":101},"([",[84,3019,3020],{"class":109},"1",[84,3022,133],{"class":105},[84,3024,2647],{"class":109},[84,3026,133],{"class":105},[84,3028,3029],{"class":109}," 3",[84,3031,3032],{"class":101},"]",[84,3034,133],{"class":105},[84,3036,3037],{"class":105}," -",[84,3039,3020],{"class":109},[84,3041,1687],{"class":101},[84,3043,3044],{"class":90},"\u002F\u002F 3\n",[19,3046,3047,3048,3050,3051,3054],{},"把 ",[81,3049,2900],{}," 写成",[62,3052,3053],{},"第一个参数","，某种程度上，这不就是 Python 嘛？",[74,3056,3060],{"className":3057,"code":3058,"language":3059,"meta":79,"style":79},"language-py shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","class foo(list):\n  def get(self, index):\n    return self[index if index >= 0 else len(self) + index]\n","py",[81,3061,3062,3067,3072],{"__ignoreMap":79},[84,3063,3064],{"class":86,"line":87},[84,3065,3066],{},"class foo(list):\n",[84,3068,3069],{"class":86,"line":94},[84,3070,3071],{},"  def get(self, index):\n",[84,3073,3074],{"class":86,"line":116},[84,3075,3076],{},"    return self[index if index >= 0 else len(self) + index]\n",[383,3078,3080],{"id":3079},"overloading","重载",[19,3082,3083],{},"有时候可能复合的类型不足以满足你描述一个函数的返回值。比方说：",[74,3085,3087],{"className":76,"code":3086,"language":78,"meta":79,"style":79},"function getPrimaryKey(el: HTMLAnchorElement | HTMLImageElement): 'href' | 'src'\n",[81,3088,3089],{"__ignoreMap":79},[84,3090,3091,3093,3096,3098,3101,3103,3106,3108,3111,3113,3115,3118,3120,3122,3124,3127],{"class":86,"line":87},[84,3092,119],{"class":97},[84,3094,3095],{"class":122}," getPrimaryKey",[84,3097,126],{"class":105},[84,3099,3100],{"class":129},"el",[84,3102,196],{"class":105},[84,3104,3105],{"class":199}," HTMLAnchorElement",[84,3107,976],{"class":105},[84,3109,3110],{"class":199}," HTMLImageElement",[84,3112,236],{"class":105},[84,3114,1051],{"class":105},[84,3116,3117],{"class":1054},"href",[84,3119,1058],{"class":105},[84,3121,976],{"class":105},[84,3123,1051],{"class":105},[84,3125,3126],{"class":1054},"src",[84,3128,3129],{"class":105},"'\n",[19,3131,3132],{},"如果像上面这样写的话，TypeScript 就会理解为：这个函数可以接受链接或者图片元素，返回的可能是两个字符串中的任意一个。",[19,3134,3135,3136,3139,3140,3143],{},"但是实际上，这里的类型是有关系的：当传入的参数为 ",[81,3137,3138],{},"HTMLAnchorElement"," 时，返回就只可能是 ",[81,3141,3142],{},"'href'"," ，反之亦然。这种情况下我们就会用到重载：",[74,3145,3147],{"className":76,"code":3146,"language":78,"meta":79,"style":79},"function getPrimaryKey(el: HTMLAnchorElement): 'href'\nfunction getPrimaryKey(el: HTMLImageElement): 'src'\n",[81,3148,3149,3171],{"__ignoreMap":79},[84,3150,3151,3153,3155,3157,3159,3161,3163,3165,3167,3169],{"class":86,"line":87},[84,3152,119],{"class":97},[84,3154,3095],{"class":122},[84,3156,126],{"class":105},[84,3158,3100],{"class":129},[84,3160,196],{"class":105},[84,3162,3105],{"class":199},[84,3164,236],{"class":105},[84,3166,1051],{"class":105},[84,3168,3117],{"class":1054},[84,3170,3129],{"class":105},[84,3172,3173,3175,3177,3179,3181,3183,3185,3187,3189,3191],{"class":86,"line":94},[84,3174,119],{"class":97},[84,3176,3095],{"class":122},[84,3178,126],{"class":105},[84,3180,3100],{"class":129},[84,3182,196],{"class":105},[84,3184,3110],{"class":199},[84,3186,236],{"class":105},[84,3188,1051],{"class":105},[84,3190,3126],{"class":1054},[84,3192,3129],{"class":105},[19,3194,3195],{},"简单地说，就是把每个分支情况都声明一遍就好了",[383,3197,3199],{"id":3198},"is-type",[81,3200,3201],{},"is type",[19,3203,3204],{},"很多时候，使用类型系统都会遇到一个头疼的问题，就是类型的收紧：",[74,3206,3208],{"className":76,"code":3207,"language":78,"meta":79,"style":79},"function isNumber(arg: unknown): boolean {}\n\nfunction run(arg: number | string) {\n  if (isNumber(arg)) {\n    arg = String(arg)\n  }\n  arg.startsWith('http:') \u002F\u002F ts-error\n}\n",[81,3209,3210,3233,3237,3260,3280,3296,3301,3325],{"__ignoreMap":79},[84,3211,3212,3214,3217,3219,3222,3224,3226,3228,3231],{"class":86,"line":87},[84,3213,119],{"class":97},[84,3215,3216],{"class":122}," isNumber",[84,3218,126],{"class":105},[84,3220,3221],{"class":129},"arg",[84,3223,196],{"class":105},[84,3225,1306],{"class":199},[84,3227,236],{"class":105},[84,3229,3230],{"class":199}," boolean",[84,3232,1154],{"class":105},[84,3234,3235],{"class":86,"line":94},[84,3236,179],{"emptyLinePlaceholder":178},[84,3238,3239,3241,3244,3246,3248,3250,3252,3254,3256,3258],{"class":86,"line":116},[84,3240,119],{"class":97},[84,3242,3243],{"class":122}," run",[84,3245,126],{"class":105},[84,3247,3221],{"class":129},[84,3249,196],{"class":105},[84,3251,200],{"class":199},[84,3253,976],{"class":105},[84,3255,497],{"class":199},[84,3257,145],{"class":105},[84,3259,148],{"class":105},[84,3261,3262,3265,3267,3270,3272,3274,3277],{"class":86,"line":151},[84,3263,3264],{"class":1463},"  if",[84,3266,696],{"class":491},[84,3268,3269],{"class":122},"isNumber",[84,3271,126],{"class":491},[84,3273,3221],{"class":101},[84,3275,3276],{"class":491},")) ",[84,3278,3279],{"class":105},"{\n",[84,3281,3282,3285,3287,3290,3292,3294],{"class":86,"line":169},[84,3283,3284],{"class":101},"    arg",[84,3286,139],{"class":105},[84,3288,3289],{"class":122}," String",[84,3291,126],{"class":491},[84,3293,3221],{"class":101},[84,3295,2262],{"class":491},[84,3297,3298],{"class":86,"line":175},[84,3299,3300],{"class":105},"  }\n",[84,3302,3303,3306,3308,3311,3313,3315,3318,3320,3322],{"class":86,"line":182},[84,3304,3305],{"class":101},"  arg",[84,3307,2248],{"class":105},[84,3309,3310],{"class":122},"startsWith",[84,3312,126],{"class":491},[84,3314,1058],{"class":105},[84,3316,3317],{"class":1054},"http:",[84,3319,1058],{"class":105},[84,3321,1687],{"class":491},[84,3323,3324],{"class":90},"\u002F\u002F ts-error\n",[84,3326,3327],{"class":86,"line":188},[84,3328,172],{"class":105},[19,3330,3331,3332,3334,3335,3337,3338,3340,3341,3343],{},"上面的代码里，尽管从代码阅读的角度可以明白，",[81,3333,3221],{}," 在执行 ",[81,3336,3310],{}," 的时候一定是一个字符串，但是类型系统并不能确定此时 ",[81,3339,3221],{}," 已经不可能是数字了。",[81,3342,3201],{}," 就是用来帮助类型系统确信的。",[19,3345,3346,3347,3350,3351,3354,3355,3358,3359,3362,3363,3366,3367,3369,3370,3372],{},"本质上来说 ",[81,3348,3349],{},"is"," 也是一种类型运算，",[81,3352,3353],{},"value is type"," 是 ",[81,3356,3357],{},"boolean"," 的一个子类型，且只能作为函数返回值的类型，但它包含了一部分",[62,3360,3361],{},"运行时","的信息。当函数返回值作为 ",[81,3364,3365],{},"true"," 使用时，",[81,3368,130],{}," 的类型将被推导为 ",[81,3371,630],{},"。例如上面的例子，我们可以改为：",[74,3374,3376],{"className":76,"code":3375,"language":78,"meta":79,"style":79},"function isNumber(arg: unknown): arg is number {}\n",[81,3377,3378],{"__ignoreMap":79},[84,3379,3380,3382,3384,3386,3388,3390,3392,3394,3397,3400,3402],{"class":86,"line":87},[84,3381,119],{"class":97},[84,3383,3216],{"class":122},[84,3385,126],{"class":105},[84,3387,3221],{"class":129},[84,3389,196],{"class":105},[84,3391,1306],{"class":199},[84,3393,236],{"class":105},[84,3395,3396],{"class":129}," arg",[84,3398,3399],{"class":105}," is",[84,3401,200],{"class":199},[84,3403,1154],{"class":105},[19,3405,3406],{},"这样 TypeScript 就能正确推导了。通常来说这个语法不是很常用，一般只在书写运行时类型判断的情况下会使用。",[14,3408,3410],{"id":3409},"type-expressions","类型表达式",[383,3412,995],{"id":3413},"index-signatures",[19,3415,3416,3417,3420,3421,3423,3424,3426,3427,3429],{},"考虑一下我们常用的 ",[81,3418,3419],{},"process.env"," 这个东西，它的类型应该怎么写呢？它的每一个键都是 ",[81,3422,1076],{},"，值也都是 ",[81,3425,1076],{},"。如果使用 ",[81,3428,471],{}," 的话，就可以通过索引签名来完成：",[74,3431,3433],{"className":76,"code":3432,"language":78,"meta":79,"style":79},"interface ProcessEnv {\n  [key: string]: string;\n}\n",[81,3434,3435,3444,3464],{"__ignoreMap":79},[84,3436,3437,3439,3442],{"class":86,"line":87},[84,3438,471],{"class":97},[84,3440,3441],{"class":199}," ProcessEnv",[84,3443,148],{"class":105},[84,3445,3446,3449,3452,3454,3456,3458,3460,3462],{"class":86,"line":94},[84,3447,3448],{"class":101},"  [",[84,3450,3451],{"class":129},"key",[84,3453,196],{"class":105},[84,3455,497],{"class":199},[84,3457,3032],{"class":101},[84,3459,196],{"class":105},[84,3461,497],{"class":199},[84,3463,113],{"class":105},[84,3465,3466],{"class":86,"line":116},[84,3467,172],{"class":105},[19,3469,3470],{},"本质上其实和 JS 里面变量索引的写法是类似的。",[19,3472,3473,3474,3476,3477,2083],{},"不仅如此，你也可以针对性的为某些 key 声明 ",[81,3475,1076],{}," 的",[62,3478,3479],{},"子类型",[74,3481,3483],{"className":76,"code":3482,"language":78,"meta":79,"style":79},"interface ProcessEnv {\n  NODE_ENV: 'production' | 'development';\n  [key: string]: string;\n}\n",[81,3484,3485,3493,3518,3536],{"__ignoreMap":79},[84,3486,3487,3489,3491],{"class":86,"line":87},[84,3488,471],{"class":97},[84,3490,3441],{"class":199},[84,3492,148],{"class":105},[84,3494,3495,3498,3500,3502,3505,3507,3509,3511,3514,3516],{"class":86,"line":94},[84,3496,3497],{"class":491},"  NODE_ENV",[84,3499,196],{"class":105},[84,3501,1051],{"class":105},[84,3503,3504],{"class":1054},"production",[84,3506,1058],{"class":105},[84,3508,976],{"class":105},[84,3510,1051],{"class":105},[84,3512,3513],{"class":1054},"development",[84,3515,1058],{"class":105},[84,3517,113],{"class":105},[84,3519,3520,3522,3524,3526,3528,3530,3532,3534],{"class":86,"line":116},[84,3521,3448],{"class":101},[84,3523,3451],{"class":129},[84,3525,196],{"class":105},[84,3527,497],{"class":199},[84,3529,3032],{"class":101},[84,3531,196],{"class":105},[84,3533,497],{"class":199},[84,3535,113],{"class":105},[84,3537,3538],{"class":86,"line":151},[84,3539,172],{"class":105},[383,3541,3543],{"id":3542},"mapped-types","映射的对象类型",[19,3545,3546],{},"对于类型别名而言，有一个类似的语法，但实际上完成了不同的操作",[74,3548,3550],{"className":76,"code":3549,"language":78,"meta":79,"style":79},"type ProcessEnv = {\n  [K in string]: string\n}\n",[81,3551,3552,3562,3580],{"__ignoreMap":79},[84,3553,3554,3556,3558,3560],{"class":86,"line":87},[84,3555,630],{"class":97},[84,3557,3441],{"class":199},[84,3559,139],{"class":105},[84,3561,148],{"class":105},[84,3563,3564,3566,3569,3572,3574,3576,3578],{"class":86,"line":94},[84,3565,3448],{"class":101},[84,3567,3568],{"class":199},"K",[84,3570,3571],{"class":105}," in",[84,3573,497],{"class":199},[84,3575,3032],{"class":101},[84,3577,196],{"class":105},[84,3579,958],{"class":199},[84,3581,3582],{"class":86,"line":116},[84,3583,172],{"class":105},[19,3585,3586,3587,3590,3591,3593,3594,3597],{},"上面这个类型意味着 ",[81,3588,3589],{},"ProcessEnv"," 的键名类型为 ",[81,3592,1076],{},"，值的类型都是 ",[81,3595,3596],{},"Object","。但是注意这里有一些细节的差别：",[39,3599,3600],{},[42,3601,3602,3603,3606,3607,3610],{},"映射中你可以将任意的 ",[81,3604,3605],{},"string | number | symbol"," 的子类型写在 ",[81,3608,3609],{},"in"," 的后面，比如你可以写一个表达式：",[74,3612,3614],{"className":76,"code":3613,"language":78,"meta":79,"style":79},"type Foo = {\n  [K in 'abc' | 'xyz']: string\n}\n",[81,3615,3616,3626,3655],{"__ignoreMap":79},[84,3617,3618,3620,3622,3624],{"class":86,"line":87},[84,3619,630],{"class":97},[84,3621,2095],{"class":199},[84,3623,139],{"class":105},[84,3625,148],{"class":105},[84,3627,3628,3630,3632,3634,3636,3638,3640,3642,3644,3647,3649,3651,3653],{"class":86,"line":94},[84,3629,3448],{"class":101},[84,3631,3568],{"class":199},[84,3633,3571],{"class":105},[84,3635,1051],{"class":105},[84,3637,2723],{"class":1054},[84,3639,1058],{"class":105},[84,3641,976],{"class":105},[84,3643,1051],{"class":105},[84,3645,3646],{"class":1054},"xyz",[84,3648,1058],{"class":105},[84,3650,3032],{"class":101},[84,3652,196],{"class":105},[84,3654,958],{"class":199},[84,3656,3657],{"class":86,"line":116},[84,3658,172],{"class":105},[19,3660,3661],{},"但是在索引类型中，你只能引用某一种基础类型。",[39,3663,3664],{},[42,3665,3666],{},"映射中你可以在值里引用键的具体类型，例如：",[74,3668,3670],{"className":76,"code":3669,"language":78,"meta":79,"style":79},"type Bar = {\n  [K in keyof Foo]?: Foo[K]\n}\n",[81,3671,3672,3682,3706],{"__ignoreMap":79},[84,3673,3674,3676,3678,3680],{"class":86,"line":87},[84,3675,630],{"class":97},[84,3677,2150],{"class":199},[84,3679,139],{"class":105},[84,3681,148],{"class":105},[84,3683,3684,3686,3688,3690,3692,3694,3696,3698,3700,3702,3704],{"class":86,"line":94},[84,3685,3448],{"class":101},[84,3687,3568],{"class":199},[84,3689,3571],{"class":105},[84,3691,1805],{"class":105},[84,3693,2095],{"class":199},[84,3695,3032],{"class":101},[84,3697,322],{"class":105},[84,3699,2095],{"class":199},[84,3701,1744],{"class":101},[84,3703,3568],{"class":199},[84,3705,1994],{"class":101},[84,3707,3708],{"class":86,"line":116},[84,3709,172],{"class":105},[383,3711,3713,2128,3715,79],{"id":3712},"extends-and-infer",[81,3714,866],{},[81,3716,3717],{},"infer",[19,3719,3720],{},"一个只有加减乘除的玩具并不能算作程序语言，想要实现一个程序语言最重要的就是要能执行条件逻辑。有时候你想要实现这样的东西：",[74,3722,3725],{"className":3723,"code":3724,"language":1826},[1824],"\u002F\u002F 这可不是 TypeScript\nif (T is number) {\n  type Foo\u003CT> = string\n} else {\n  type Foo\u003CT> = never\n}\n",[81,3726,3724],{"__ignoreMap":79},[19,3728,3729,3730,3732],{},"TypeScript 一个很常见的特性就是 ",[81,3731,866],{}," 三元组。上面的逻辑可以实现为：",[74,3734,3736],{"className":76,"code":3735,"language":78,"meta":79,"style":79},"type Foo\u003CT> = T extends number ? string : never\n",[81,3737,3738],{"__ignoreMap":79},[84,3739,3740,3742,3744,3746,3748,3750,3752,3754,3756,3758,3760,3762,3764],{"class":86,"line":87},[84,3741,630],{"class":97},[84,3743,2095],{"class":199},[84,3745,1895],{"class":105},[84,3747,2057],{"class":199},[84,3749,1906],{"class":105},[84,3751,139],{"class":105},[84,3753,2064],{"class":199},[84,3755,916],{"class":97},[84,3757,200],{"class":199},[84,3759,2978],{"class":105},[84,3761,497],{"class":199},[84,3763,2983],{"class":105},[84,3765,3766],{"class":199}," never\n",[19,3768,3769,3770,3772,3773,3775],{},"注意，这可不是 JS 里面的三元操作符，相反，只有带上 ",[81,3771,866],{}," 才是合法的语法；另外，",[81,3774,866],{}," 意味着，只要前面的类型是后面类型的子类就行了，而不必是充分必要条件。",[19,3777,3778,3779],{},"有了这个特性，你可以实现很多有意思的操作，例如",[62,3780,3781],{},"获取数组的元素类型",[74,3783,3785],{"className":76,"code":3784,"language":78,"meta":79,"style":79},"type ElementType\u003CT> = T extends any[] ? T[number] : never\n",[81,3786,3787],{"__ignoreMap":79},[84,3788,3789,3791,3794,3796,3798,3800,3802,3804,3806,3808,3811,3814,3816,3818,3820,3822,3824],{"class":86,"line":87},[84,3790,630],{"class":97},[84,3792,3793],{"class":199}," ElementType",[84,3795,1895],{"class":105},[84,3797,2057],{"class":199},[84,3799,1906],{"class":105},[84,3801,139],{"class":105},[84,3803,2064],{"class":199},[84,3805,916],{"class":97},[84,3807,1149],{"class":199},[84,3809,3810],{"class":101},"[] ",[84,3812,3813],{"class":105},"?",[84,3815,2064],{"class":199},[84,3817,1744],{"class":101},[84,3819,1679],{"class":199},[84,3821,1083],{"class":101},[84,3823,196],{"class":105},[84,3825,3766],{"class":199},[19,3827,3828,3829],{},"但你一定不满足于此。例如，你想要实现",[62,3830,3831],{},"获取函数的返回值类型",[74,3833,3835],{"className":76,"code":3834,"language":78,"meta":79,"style":79},"type ReturnType\u003CT> = T extends (...args: any[]) => any ? [the second any] : never\n",[81,3836,3837],{"__ignoreMap":79},[84,3838,3839,3841,3844,3846,3848,3850,3852,3854,3856,3859,3862,3864,3866,3869,3871,3873,3875,3877,3879,3882,3885,3887,3889,3891],{"class":86,"line":87},[84,3840,630],{"class":97},[84,3842,3843],{"class":199}," ReturnType",[84,3845,1895],{"class":105},[84,3847,2057],{"class":199},[84,3849,1906],{"class":105},[84,3851,139],{"class":105},[84,3853,2064],{"class":199},[84,3855,916],{"class":97},[84,3857,3858],{"class":105}," (...",[84,3860,3861],{"class":129},"args",[84,3863,196],{"class":105},[84,3865,1149],{"class":199},[84,3867,3868],{"class":101},"[]",[84,3870,145],{"class":105},[84,3872,715],{"class":97},[84,3874,1149],{"class":199},[84,3876,2978],{"class":105},[84,3878,1073],{"class":101},[84,3880,3881],{"class":199},"the",[84,3883,3884],{"class":199}," second",[84,3886,1149],{"class":199},[84,3888,1083],{"class":101},[84,3890,196],{"class":105},[84,3892,3766],{"class":199},[19,3894,3895,3896,3899,3900,3903],{},"这这这，这个 ",[84,3897,3898],{},"the second any"," 要咋写呀？真希望有一个类似于正则表达式替换的 ",[81,3901,3902],{},"$n"," 这种东西呀！",[19,3905,3906,3908],{},[81,3907,3717],{}," 关键字实际上就是干这个的：",[74,3910,3912],{"className":76,"code":3911,"language":78,"meta":79,"style":79},"type ReturnType\u003CT> = T extends (...args: any[]) => infer R ? R : never\n",[81,3913,3914],{"__ignoreMap":79},[84,3915,3916,3918,3920,3922,3924,3926,3928,3930,3932,3934,3936,3938,3940,3942,3944,3946,3949,3952,3954,3956,3958],{"class":86,"line":87},[84,3917,630],{"class":97},[84,3919,3843],{"class":199},[84,3921,1895],{"class":105},[84,3923,2057],{"class":199},[84,3925,1906],{"class":105},[84,3927,139],{"class":105},[84,3929,2064],{"class":199},[84,3931,916],{"class":97},[84,3933,3858],{"class":105},[84,3935,3861],{"class":129},[84,3937,196],{"class":105},[84,3939,1149],{"class":199},[84,3941,3868],{"class":101},[84,3943,145],{"class":105},[84,3945,715],{"class":97},[84,3947,3948],{"class":105}," infer",[84,3950,3951],{"class":199}," R",[84,3953,2978],{"class":105},[84,3955,3951],{"class":199},[84,3957,2983],{"class":105},[84,3959,3766],{"class":199},[19,3961,3962,3963,3965,3966,3968,3969,3971,3972,3974],{},"你可以在 ",[81,3964,866],{}," 三元组的 ",[81,3967,866],{}," 右侧利用 ",[81,3970,3717],{}," 声明一个变量作为占位符，然后在 ",[81,3973,3813],{}," 后面使用它。",[14,3976,3978],{"id":3977},"built-in-types","内置的工具类型",[19,3980,3981],{},"事实上，上面介绍的很多特性使用的场景都很有限，所以在这些场景里，TypeScript 已经内置了一些工具类型，尽管使用就可以了：",[39,3983,3984,3997,4007,4022,4041,4053,4065,4079],{},[42,3985,3986,3989],{},[81,3987,3988],{},"PropertyKey",[39,3990,3991],{},[42,3992,3993,3994,3996],{},"就是 ",[81,3995,3605],{}," 的别名",[42,3998,3999,4002],{},[81,4000,4001],{},"ThisParameterType\u003CT>",[39,4003,4004],{},[42,4005,4006],{},"当 T 是一个函数时，返回 T 绑定的 this 类型",[42,4008,4009,4012],{},[81,4010,4011],{},"OmitThisParameter\u003CT>",[39,4013,4014],{},[42,4015,4016,4017,4019,4020,2901],{},"返回未绑定 ",[81,4018,2900],{}," 的 ",[81,4021,2057],{},[42,4023,4024,2128,4027,4030],{},[81,4025,4026],{},"PromiseLike\u003CT>",[81,4028,4029],{},"ArrayLike\u003CT>",[39,4031,4032],{},[42,4033,4034,4035,4037,4038,4040],{},"前者是一个 thenable 的对象类型（将会 resolve ",[81,4036,2057],{}," 类型的值），后者是 ",[81,4039,2057],{}," 的类数组",[42,4042,4043,4046],{},[81,4044,4045],{},"Partial\u003CT>",[39,4047,4048],{},[42,4049,4050,4051,2901],{},"可以返回一个所有属性都可以缺少的 ",[81,4052,2057],{},[42,4054,4055,4058],{},[81,4056,4057],{},"Required\u003CT>",[39,4059,4060],{},[42,4061,4062,4063,2901],{},"返回一个所有属性都不可缺少的 ",[81,4064,2057],{},[42,4066,4067,4070],{},[81,4068,4069],{},"Readonly\u003CT>",[39,4071,4072],{},[42,4073,4074,4075,4019,4077,2901],{},"返回一个所有属性都 ",[81,4076,2138],{},[81,4078,2057],{},[42,4080,4081,4084],{},[81,4082,4083],{},"Pick\u003CT, K>",[39,4085,4086],{},[42,4087,4088,4089,4091,4092,4094],{},"从对象 ",[81,4090,2057],{}," 中选取 ",[81,4093,3568],{}," 这些键",[74,4096,4098],{"className":76,"code":4097,"language":78,"meta":79,"style":79},"type Foo = Pick\u003C{ a: string, b: number, c: boolean }, 'a' | 'b'>\n\u002F\u002F { a: string, b: number }\n",[81,4099,4100,4159],{"__ignoreMap":79},[84,4101,4102,4104,4106,4108,4111,4114,4116,4118,4120,4122,4125,4127,4129,4131,4134,4136,4138,4141,4143,4145,4147,4149,4151,4154,4156],{"class":86,"line":87},[84,4103,630],{"class":97},[84,4105,2095],{"class":199},[84,4107,139],{"class":105},[84,4109,4110],{"class":199}," Pick",[84,4112,4113],{"class":105},"\u003C{",[84,4115,1546],{"class":491},[84,4117,196],{"class":105},[84,4119,497],{"class":199},[84,4121,133],{"class":105},[84,4123,4124],{"class":491}," b",[84,4126,196],{"class":105},[84,4128,200],{"class":199},[84,4130,133],{"class":105},[84,4132,4133],{"class":491}," c",[84,4135,196],{"class":105},[84,4137,3230],{"class":199},[84,4139,4140],{"class":105}," },",[84,4142,1051],{"class":105},[84,4144,23],{"class":1054},[84,4146,1058],{"class":105},[84,4148,976],{"class":105},[84,4150,1051],{"class":105},[84,4152,4153],{"class":1054},"b",[84,4155,1058],{"class":105},[84,4157,4158],{"class":105},">\n",[84,4160,4161],{"class":86,"line":94},[84,4162,4163],{"class":90},"\u002F\u002F { a: string, b: number }\n",[39,4165,4166,4181],{},[42,4167,4168,4171],{},[81,4169,4170],{},"Record\u003CK, T>",[39,4172,4173],{},[42,4174,4175,4176,4178,4179],{},"就是我们通常所说的对象类型，键的类型为 ",[81,4177,3568],{},"，值的类型为 ",[81,4180,2057],{},[42,4182,4183,4186],{},[81,4184,4185],{},"Exclude\u003CT, U>",[39,4187,4188],{},[42,4189,4190,4191,4193,4194],{},"从 ",[81,4192,2057],{}," 中排除 ",[81,4195,4196],{},"U",[74,4198,4200],{"className":76,"code":4199,"language":78,"meta":79,"style":79},"type Foo = Exclude\u003C'a' | 'b' | 'c', 'a' | 'b'>\n\u002F\u002F 'c'\n",[81,4201,4202,4256],{"__ignoreMap":79},[84,4203,4204,4206,4208,4210,4213,4215,4217,4219,4221,4223,4225,4227,4229,4231,4233,4236,4238,4240,4242,4244,4246,4248,4250,4252,4254],{"class":86,"line":87},[84,4205,630],{"class":97},[84,4207,2095],{"class":199},[84,4209,139],{"class":105},[84,4211,4212],{"class":199}," Exclude",[84,4214,1895],{"class":105},[84,4216,1058],{"class":105},[84,4218,23],{"class":1054},[84,4220,1058],{"class":105},[84,4222,976],{"class":105},[84,4224,1051],{"class":105},[84,4226,4153],{"class":1054},[84,4228,1058],{"class":105},[84,4230,976],{"class":105},[84,4232,1051],{"class":105},[84,4234,4235],{"class":1054},"c",[84,4237,1058],{"class":105},[84,4239,133],{"class":105},[84,4241,1051],{"class":105},[84,4243,23],{"class":1054},[84,4245,1058],{"class":105},[84,4247,976],{"class":105},[84,4249,1051],{"class":105},[84,4251,4153],{"class":1054},[84,4253,1058],{"class":105},[84,4255,4158],{"class":105},[84,4257,4258],{"class":86,"line":94},[84,4259,4260],{"class":90},"\u002F\u002F 'c'\n",[39,4262,4263],{},[42,4264,4265,4268],{},[81,4266,4267],{},"Extract\u003CT, U>",[39,4269,4270],{},[42,4271,4272,4273,4276,4277,4279,4280],{},"反向 ",[81,4274,4275],{},"Exclude","，从 ",[81,4278,2057],{}," 中提出 ",[81,4281,4196],{},[74,4283,4285],{"className":76,"code":4284,"language":78,"meta":79,"style":79},"type Foo = Extract\u003C'a' | 'b' | 'c', 'a' | 'b' | 'd'>\n\u002F\u002F 'a' | 'b'\n",[81,4286,4287,4349],{"__ignoreMap":79},[84,4288,4289,4291,4293,4295,4298,4300,4302,4304,4306,4308,4310,4312,4314,4316,4318,4320,4322,4324,4326,4328,4330,4332,4334,4336,4338,4340,4342,4345,4347],{"class":86,"line":87},[84,4290,630],{"class":97},[84,4292,2095],{"class":199},[84,4294,139],{"class":105},[84,4296,4297],{"class":199}," Extract",[84,4299,1895],{"class":105},[84,4301,1058],{"class":105},[84,4303,23],{"class":1054},[84,4305,1058],{"class":105},[84,4307,976],{"class":105},[84,4309,1051],{"class":105},[84,4311,4153],{"class":1054},[84,4313,1058],{"class":105},[84,4315,976],{"class":105},[84,4317,1051],{"class":105},[84,4319,4235],{"class":1054},[84,4321,1058],{"class":105},[84,4323,133],{"class":105},[84,4325,1051],{"class":105},[84,4327,23],{"class":1054},[84,4329,1058],{"class":105},[84,4331,976],{"class":105},[84,4333,1051],{"class":105},[84,4335,4153],{"class":1054},[84,4337,1058],{"class":105},[84,4339,976],{"class":105},[84,4341,1051],{"class":105},[84,4343,4344],{"class":1054},"d",[84,4346,1058],{"class":105},[84,4348,4158],{"class":105},[84,4350,4351],{"class":86,"line":94},[84,4352,4353],{"class":90},"\u002F\u002F 'a' | 'b'\n",[39,4355,4356],{},[42,4357,4358,4361],{},[81,4359,4360],{},"Omit\u003CT, K>",[39,4362,4363],{},[42,4364,4272,4365,4368,4369,4371,4372,4094],{},[81,4366,4367],{},"Pick","，从对象 ",[81,4370,2057],{}," 中省略 ",[81,4373,3568],{},[74,4375,4377],{"className":76,"code":4376,"language":78,"meta":79,"style":79},"type Foo = Omit\u003C{ a: string, b: number, c: boolean }, 'a' | 'b'>\n\u002F\u002F { c: boolean }\n",[81,4378,4379,4432],{"__ignoreMap":79},[84,4380,4381,4383,4385,4387,4390,4392,4394,4396,4398,4400,4402,4404,4406,4408,4410,4412,4414,4416,4418,4420,4422,4424,4426,4428,4430],{"class":86,"line":87},[84,4382,630],{"class":97},[84,4384,2095],{"class":199},[84,4386,139],{"class":105},[84,4388,4389],{"class":199}," Omit",[84,4391,4113],{"class":105},[84,4393,1546],{"class":491},[84,4395,196],{"class":105},[84,4397,497],{"class":199},[84,4399,133],{"class":105},[84,4401,4124],{"class":491},[84,4403,196],{"class":105},[84,4405,200],{"class":199},[84,4407,133],{"class":105},[84,4409,4133],{"class":491},[84,4411,196],{"class":105},[84,4413,3230],{"class":199},[84,4415,4140],{"class":105},[84,4417,1051],{"class":105},[84,4419,23],{"class":1054},[84,4421,1058],{"class":105},[84,4423,976],{"class":105},[84,4425,1051],{"class":105},[84,4427,4153],{"class":1054},[84,4429,1058],{"class":105},[84,4431,4158],{"class":105},[84,4433,4434],{"class":86,"line":94},[84,4435,4436],{"class":90},"\u002F\u002F { c: boolean }\n",[39,4438,4439],{},[42,4440,4441,4444],{},[81,4442,4443],{},"Parameters\u003CT>",[39,4445,4446],{},[42,4447,4448,4449,4451],{},"返回函数 ",[81,4450,2057],{}," 的参数",[74,4453,4455],{"className":76,"code":4454,"language":78,"meta":79,"style":79},"type Foo = Paramaters\u003C(a: string, b: number) => boolean>\n\u002F\u002F string | number\n",[81,4456,4457,4493],{"__ignoreMap":79},[84,4458,4459,4461,4463,4465,4468,4471,4473,4475,4477,4479,4481,4483,4485,4487,4489,4491],{"class":86,"line":87},[84,4460,630],{"class":97},[84,4462,2095],{"class":199},[84,4464,139],{"class":105},[84,4466,4467],{"class":199}," Paramaters",[84,4469,4470],{"class":105},"\u003C(",[84,4472,23],{"class":129},[84,4474,196],{"class":105},[84,4476,497],{"class":199},[84,4478,133],{"class":105},[84,4480,4124],{"class":129},[84,4482,196],{"class":105},[84,4484,200],{"class":199},[84,4486,145],{"class":105},[84,4488,715],{"class":97},[84,4490,3230],{"class":199},[84,4492,4158],{"class":105},[84,4494,4495],{"class":86,"line":94},[84,4496,4497],{"class":90},"\u002F\u002F string | number\n",[39,4499,4500],{},[42,4501,4502,4505],{},[81,4503,4504],{},"ReturnType\u003CT>",[39,4506,4507],{},[42,4508,4448,4509,4511],{},[81,4510,2057],{}," 的返回值",[74,4513,4515],{"className":76,"code":4514,"language":78,"meta":79,"style":79},"type Foo = ReturnType\u003C(a: string, b: number) => boolean>\n\u002F\u002F boolean\n",[81,4516,4517,4551],{"__ignoreMap":79},[84,4518,4519,4521,4523,4525,4527,4529,4531,4533,4535,4537,4539,4541,4543,4545,4547,4549],{"class":86,"line":87},[84,4520,630],{"class":97},[84,4522,2095],{"class":199},[84,4524,139],{"class":105},[84,4526,3843],{"class":199},[84,4528,4470],{"class":105},[84,4530,23],{"class":129},[84,4532,196],{"class":105},[84,4534,497],{"class":199},[84,4536,133],{"class":105},[84,4538,4124],{"class":129},[84,4540,196],{"class":105},[84,4542,200],{"class":199},[84,4544,145],{"class":105},[84,4546,715],{"class":97},[84,4548,3230],{"class":199},[84,4550,4158],{"class":105},[84,4552,4553],{"class":86,"line":94},[84,4554,4555],{"class":90},"\u002F\u002F boolean\n",[39,4557,4558],{},[42,4559,4560,4563],{},[81,4561,4562],{},"InstanceType\u003CT>",[39,4564,4565],{},[42,4566,4567,4568,4570],{},"返回类 ",[81,4569,2057],{}," 的实例类型",[74,4572,4574],{"className":76,"code":4573,"language":78,"meta":79,"style":79},"type Foo = InstanceType\u003CObjectConstructor>\n\u002F\u002F Object\n",[81,4575,4576,4594],{"__ignoreMap":79},[84,4577,4578,4580,4582,4584,4587,4589,4592],{"class":86,"line":87},[84,4579,630],{"class":97},[84,4581,2095],{"class":199},[84,4583,139],{"class":105},[84,4585,4586],{"class":199}," InstanceType",[84,4588,1895],{"class":105},[84,4590,4591],{"class":199},"ObjectConstructor",[84,4593,4158],{"class":105},[84,4595,4596],{"class":86,"line":94},[84,4597,4598],{"class":90},"\u002F\u002F Object\n",[39,4600,4601],{},[42,4602,4603,4606],{},[81,4604,4605],{},"ThisType\u003CT>",[39,4607,4608,4616],{},[42,4609,4610,4611,4613,4614,2901],{},"用来将一个对象下的所有属性的 ",[81,4612,2900],{}," 都声明为 ",[81,4615,2057],{},[42,4617,4618,4619,4622],{},"可以考虑如何声明 Vue 组件选项的 ",[81,4620,4621],{},"methods"," 这个对象的类型",[4624,4625,4626],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":79,"searchDepth":94,"depth":94,"links":4628},[4629,4630,4631,4640,4641,4642,4648,4654],{"id":16,"depth":94,"text":17},{"id":33,"depth":94,"text":34},{"id":68,"depth":94,"text":69,"children":4632},[4633,4634,4635,4636,4637,4638,4639],{"id":385,"depth":116,"text":386},{"id":464,"depth":116,"text":465},{"id":1017,"depth":116,"text":1018},{"id":1089,"depth":116,"text":1090},{"id":1100,"depth":116,"text":1101},{"id":1636,"depth":116,"text":1637},{"id":2075,"depth":116,"text":2076},{"id":2184,"depth":94,"text":2185},{"id":2339,"depth":94,"text":2340},{"id":2893,"depth":94,"text":2894,"children":4643},[4644,4646,4647],{"id":2897,"depth":116,"text":4645},"this 类型",{"id":3079,"depth":116,"text":3080},{"id":3198,"depth":116,"text":3201},{"id":3409,"depth":94,"text":3410,"children":4649},[4650,4651,4652],{"id":3413,"depth":116,"text":995},{"id":3542,"depth":116,"text":3543},{"id":3712,"depth":116,"text":4653},"extends 和 infer",{"id":3977,"depth":94,"text":3978},"2020-09-03","md",{},"\u002Fposts\u002Ftypescript-development-tips",{"title":5,"description":79},"posts\u002F2020\u002Ftypescript-development-tips","16uZ_K4CKQQR7Aho8dh7zZru-17p5dMpsfx9fip6Iyc",[4663,4667],{"title":4664,"path":4665,"stem":4666,"children":-1},"JS Why Not 系列","\u002Fposts\u002Fjs-why-not","posts\u002F2020\u002Fjs-why-not",{"title":4668,"path":4669,"stem":4670,"children":-1},"如何编写单元测试","\u002Fposts\u002Fhow-to-write-unit-tests","posts\u002F2020\u002Fhow-to-write-unit-tests",1792500630764]