TypeScript 4.1 RC 版本发布:带来了令人兴奋的新特性

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"本月3日,微软正式发布了TypeScript 4.1的发布候选(RC)版本。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"需要安装这个RC版的同学,可以通过"},{"type":"link","attrs":{"href":"https:\/\/www.nuget.org\/packages\/Microsoft.TypeScript.MSBuild","title":"xxx","type":null},"content":[{"type":"text","text":"NuGet"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"获取,或使用npm命令:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"npm install typescript@rc"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"你还可以通过以下方式获得编辑器支持:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"下载Visual Studio 2019\/2017"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"按"},{"type":"link","attrs":{"href":"https:\/\/code.visualstudio.com\/Docs\/languages\/typescript#_using-newer-typescript-versions","title":"xxx","type":null},"content":[{"type":"text","text":"Visual Studio Code"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"和"},{"type":"link","attrs":{"href":"https:\/\/github.com\/Microsoft\/TypeScript-Sublime-Plugin\/#note-using-different-versions-of-typescript","title":"xxx","type":null},"content":[{"type":"text","text":"Sublime Text"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"的指南操作。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"在这个版本中我们提供了一些令人兴奋的新特性、新的检查标志、编辑器生产力更新和性能改进。下面就来看看4.1为我们准备了哪些内容!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"引入字符串模板类型"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在映射类型中加入键重映射"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"递归条件类型"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"新增检查索引访问功能 --noUncheckedIndexedAccess"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用 path 启用路径映射时可以不指定 baseUrl"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"checkJs 现在默认意味着 allowJs,不再需要同时设置 checkJs 和 allowJs"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"支持 React 17 的 JSX 功能"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"JSDoc @see 标签的编辑器支持"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"重大更改"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"模板字面量(Template Literal)类型"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"我们可以使用TypeScript中的字符串字面量类型,来建模需要一组特定字符串的函数和API。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"function setVerticalAlignment(color: \"top\" | \"middle\" | \"bottom\") {\n \/\/ ...\n}\n\n\nsetVerticalAlignment(\"middel\");\n\/\/ ~~~~~~~~\n\/\/ error: Argument of type '\"middel\"' is not assignable to\n\/\/ parameter of type '\"top\" | \"middle\" | \"bottom\"'."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"这个特性很好用,因为字符串字面量类型可以对我们的字符串值进行基本的拼写检查。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"另一个好处是,字符串字面量可以用作映射类型中的属性名称。从这个意义上讲,它们也可用作构建块。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"type Options = {\n [K in \"noImplicitAny\" | \"strictNullChecks\" | \"strictFunctionTypes\"]?: boolean\n};\n\/\/ same as\n\/\/ type Options = {\n\/\/ noImplicitAny?: boolean,\n\/\/ strictNullChecks?: boolean,\n\/\/ strictFunctionTypes?: boolean\n\/\/ };"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"字符串字面量类型还可以用作另一种构建块:构建其他字符串字面量类型。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"所以TypeScript 4.1引入了模板字面量字符串类型。它的语法和JavaScript中的模板字面量字符串是一样的,只是用在类型的场景中。当它用于字面量的具体类型(concrete type)时,它会串联内容来生成一个新的字符串字面量类型。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"type World = \"world\";\n\n\ntype Greeting = `hello ${World}`;\n\/\/ same as\n\/\/ type Greeting = \"hello world\";"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"在替代位置有联合类型呢?它会生成可以由每个联合成员表示的所有可能的字符串字面量的集合。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"type Color = \"red\" | \"blue\";\ntype Quantity = \"one\" | \"two\";\n\n\ntype SeussFish = `${Quantity | Color} fish`;\n\/\/ same as\n\/\/ type SeussFish = \"one fish\" | \"two fish\"\n\/\/ | \"red fish\" | \"blue fish\";5"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"这个特性的用途远不止发行说明里的这点小例子。例如,几个用于UI组件的库有一种在其API中同时指定垂直和水平对齐方式的方法,一般是用两个分别表示横纵轴对齐的字符串连接,例如“bottom- right”。垂直对齐可选的有“top”“middle”和“bottom”,水平对齐有“left”“center”和“right”,加起来有9个字符串选项,前后字符串之间都用破折号连接。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"type VerticalAlignment = \"top\" | \"middle\" | \"bottom\";\ntype HorizontalAlignment = \"left\" | \"center\" | \"right\";\n\n\n\/\/ Takes\n\/\/ | \"top-left\" | \"top-center\" | \"top-right\"\n\/\/ | \"middle-left\" | \"middle-center\" | \"middle-right\"\n\/\/ | \"bottom-left\" | \"bottom-center\" | \"bottom-right\"\ndeclare function setAlignment(value: `${VerticalAlignment}-${HorizontalAlignment}`): void;\n\n\nsetAlignment(\"top-left\"); \/\/ works!\nsetAlignment(\"top-middel\"); \/\/ error!\nsetAlignment(\"top-pot\"); \/\/ error! but good doughnuts if you're ever in Seattl"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"虽然这类API可用的有很多,但我们可以手动把这些选项都写出来,所以这个例子还是偏玩具一些的。实际上,如果只有9个字符串可选那没什么大不了。但当你需要大量字符串时,应考虑提前自动生成它们,这样就用不着那么多类型检查了(或只使用string,这更容易理解)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"这个特性的一个很有价值的用途是自动态创建新的字符串字面量。例如,想象一个makeWatchedObject API,它接收一个对象并生成一个几乎相同的对象,但加了一个新的on方法来检测属性的更改。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"let person = makeWatchedObject({\n firstName: \"Homer\",\n age: 42, \/\/ give-or-take\n location: \"Springfield\",\n});\n\n\nperson.on(\"firstNameChanged\", () => {\n console.log(`firstName was changed!`);\n});"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"注意,on会侦听事件“firstNameChanged”,而不仅仅是“firstName”。我们如何对其类型化呢?"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"type PropEventSource = {\n on(eventName: `${string & keyof T}Changed`, callback: () => void): void;\n};\n\n\n\/\/\/ Create a \"watched object\" with an 'on' method\n\/\/\/ so that you can watch for changes to properties.\ndeclare function makeWatchedObject(obj: T): T & PropEventSource;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"这样,当我们赋予错误的属性时,构建出的东西就会报错!"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"\/\/ error!\nperson.on(\"firstName\", () => {\n});\n\n\n\/\/ error!\nperson.on(\"frstNameChanged\", () => {\n});"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"我们还可以在模板字面量类型里做一些特殊的事情:我们可以从替换位置做推断。我们可以把最后一个示例通用化,从eventName字符串的各个部分做推断,以找出关联的属性。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"type PropEventSource = {\n on\n (eventName: `${K}Changed`, callback: (newValue: T[K]) => void ): void;\n};\n\n\ndeclare function makeWatchedObject(obj: T): T & PropEventSource;\n\n\nlet person = makeWatchedObject({\n firstName: \"Homer\",\n age: 42,\n location: \"Springfield\",\n});\n\n\n\/\/ works! 'newName' is typed as 'string'\nperson.on(\"firstNameChanged\", newName => {\n \/\/ 'newName' has the type of 'firstName'\n console.log(`new name is ${newName.toUpperCase()}`);\n});\n\n\n\/\/ works! 'newAge' is typed as 'number'\nperson.on(\"ageChanged\", newAge => {\n if (newAge < 0) {\n console.log(\"warning! negative age\");\n }"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"在这里我们把on变成了一种通用方法。当用户使用字符串“firstNameChanged”进行调用时,TypeScript会尝试推断K的正确类型。为此,它将K与“Changed”之前的内容进行匹配,并推断字符串“firstName”。当 TypeScript推断出来后,on方法可以获取原始对象上的firstName类型,在这里是string。类似地,当我们使用“ageChanged”调用时,它会找到属性age的类型(即number)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"推断可以有多种组合方式,通常是解构字符串,并以多种方式对其进行重构。实际上,为了帮助大家修改这些字符串字面量类型,我们添加了一些新的实用程序类型别名,用于修改字母中的大小写(也就是转换为小写和大写字符)。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"type EnthusiasticGreeting = `${Uppercase}`\n\n\ntype HELLO = EnthusiasticGreeting;\n\/\/ same as\n\/\/ type HELLO = \"HELLO\";"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"新的类型别名为Uppercase、Lowercase、Capitalize和Uncapitalize。前两个会转换字符串中的每个字符,后两个仅转换字符串中的第一个字符。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"欲了解更多信息,请参见原始的"},{"type":"link","attrs":{"href":"https:\/\/github.com\/microsoft\/TypeScript\/pull\/40336","title":"xxx","type":null},"content":[{"type":"text","text":"拉取请求"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"和进行中的"},{"type":"link","attrs":{"href":"https:\/\/github.com\/microsoft\/TypeScript\/pull\/40580","title":"xxx","type":null},"content":[{"type":"text","text":"拉取请求"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"。"}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"映射类型中加入键重映射"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"就像刷新器一样,映射类型可以基于任意键创建新的对象类型:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"type Options = {\n [K in \"noImplicitAny\" | \"strictNullChecks\" | \"strictFunctionTypes\"]?: boolean\n};\n\/\/ same as\n\/\/ type Options = {\n\/\/ noImplicitAny?: boolean,\n\/\/ strictNullChecks?: boolean,\n\/\/ strictFunctionTypes?: boolean\n\/\/ };"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"或基于其他对象类型创建新的对象类型:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"\/\/\/ 'Partial' is the same as 'T', but with each property marked optional.\ntype Partial = {\n [K in keyof T]?: T[K]\n};"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"以前,映射类型只能使用你提供的键来生成新的对象类型。但很多时候你希望能够根据输入来创建新键或过滤掉键。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"因此,TypeScript 4.1允许你使用新的as子句重新映射映射类型中的键。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"type MappedTypeWithNewKeys = {\n [K in keyof T as NewKeyType]: T[K]\n \/\/ ^^^^^^^^^^^^^\n \/\/ This is the new syntax!\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"有了这个新的as子句,你可以利用模板字面量类型之类的特性,轻松地基于旧名称创建属性名称。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"type Getters = {\n [K in keyof T as `get${Capitalize}`]: () => T[K]\n};\n\n\ninterface Person {\n name: string;\n age: number;\n location: string;\n}\n\n\ntype LazyPerson = Getters"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"你甚至可以生成never来过滤掉密钥。这意味着在某些情况下,你不必使用额外的Omit帮助程序类型。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"\/\/ Remove the 'kind' property\ntype RemoveKindField = {\n [K in keyof T as Exclude]: T[K]\n};\n\n\ninterface Circle {\n kind: \"circle\";\n radius: number;\n}\n\n\ntype KindlessCircle = RemoveKindField;\n\/\/ same as\n\/\/ type KindlessCircle = {\n\/\/ radius: number;\n\/\/ }"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"欲了解更多信息,请查看GitHub上的"},{"type":"link","attrs":{"href":"https:\/\/github.com\/microsoft\/TypeScript\/pull\/40336","title":"xxx","type":null},"content":[{"type":"text","text":"原始拉取请求"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"。"}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"递归条件类型"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"在JavaScript中,经常能看到可以展开(flatten)并建立任意级别容器类型的函数。例如,考虑Promise实例上的.then()方法。.then(...)一个个展开promise,直到它找到一个“不像promise”的值,然后将该值传递给一个回调。Arrays上还有一个相对较新的flat方法,从中可以看出展开的深度能有多大。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"以前,处于各种实际因素,在TypeScript的类型系统中无法表达这一点。尽管有一些破解方法可以实现它,但最后出来的类型看起来会很奇怪。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"所以TypeScript 4.1放宽了对条件类型的一些限制——以便它们可以构建这些模式。在TypeScript 4.1中,条件类型现在可以立即在其分支中引用自身,这样我们就更容易编写递归类型别名了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"例如,如果我们想编写一个类型来获取嵌套数组的元素类型,则可以编写以下deepFlatten类型。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"type ElementType =\n T extends ReadonlyArray ? ElementType : T;\n\n\nfunction deepFlatten(x: T): ElementType[] {\n throw \"not implemented\";\n}\n\n\n\/\/ All of these return the type 'number[]':\ndeepFlatten([1, 2, 3]);\ndeepFlatten([[1], [2, 3]]);\ndeepFlatten([[1], [[2]], [[[3]]]])"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"类似地,在TypeScript 4.1中,我们可以编写一个Awaited类型来深度展开Promise。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"type Awaited = T extends PromiseLike ? Awaited : T;\n\n\n\/\/\/ Like `promise.then(...)`, but more accurate in types.\ndeclare function customThen(\n p: Promise,\n onFulfilled: (value: Awaited) => U\n): Promise>;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"请记住,尽管这些递归类型都很强大,但使用它们的时候应该小心谨慎。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"首先,这些类型可以完成很多工作,这意味着它们会增加类型检查时间。用它计算Collat​​z猜想或斐波那契数列中的数字可能很有意思,但不要放在npm的.d.ts文件里。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"除了计算量大之外,这些类型还可能在足够复杂的输入上触及内部递归深度上限。达到这一递归上限时将导致编译时错误。一般来说最好不要使用这些类型,避免写出一些在更实际的场景中会失败的代码。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"实现细节"},{"type":"link","attrs":{"href":"https:\/\/github.com\/microsoft\/TypeScript\/pull\/40002","title":"xxx","type":null},"content":[{"type":"text","text":"见此"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"。"}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"Checked Indexed Accesses"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"TypeScript有一个称为索引签名的特性。这些签名可以用来告知类型系统,用户可以访问任意命名的属性。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"interface Options {\n path: string;\n permissions: number;\n\n\n \/\/ Extra properties are caught by this index signature.\n [propName: string]: string | number;\n}\n\n\nfunction checkOptions(opts: Options) {\n opts.path \/\/ string\n opts.permissions \/\/ number\n\n\n \/\/ These are all allowed too!\n \/\/ They have the type 'string | number'.\n opts.yadda.toString();\n opts[\"foo bar baz\"].toString();\n opts[Math.random()].toString();"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"在上面的示例中,Options有一个索引签名,其含义是任何尚未列出的accessed属性都应具有string | number类型。理想情况下(代码假定你知道自己在干什么)这很方便,但事实是,JavaScript中的大多数值并不能完整支持所有潜在的属性名称。例如,大多数类型都不会像前面的示例那样,有一个Math.random()创建的属性键的值。对于许多用户而言,这种行为是超乎预料的,并且会感觉它没有充分利用--strictNullChecks的严格检查。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"因此,TypeScript 4.1加入了一个名为--noUncheckedIndexedAccess的新标志。在这种新模式下,每个属性访问(如foo.bar)或索引访问(如foo[\"bar\"])都被认为可能是undefined的。这意味着在我们的最后一个示例中,opts.yadda的类型为string | number | undefined,而不只是string | number。如果你需要访问该属性,则必须先检查其是否存在,或者使用非null断言运算符(后缀 ! 字符)。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"\/\/ Checking if it's really there first.\nif (opts.yadda) {\n console.log(opts.yadda.toString());\n}\n\n\n\n\n\/\/ Basically saying \"trust me I know what I'm doing\"\n\/\/ with the '!' non-null assertion operator.\nopts.yadda!.toString()"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"使用--noUncheckedIndexedAccess的一个后果是,即使在边界检查循环中,也会更严格地检查对数组的索引。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"function screamLines(strs: string[]) {\n \/\/ this will have issues\n for (let i = 0; i < strs.length; i++) {\n console.log(strs[i].toUpperCase());\n \/\/ ~~~~~~~\n \/\/ error! Object is possibly 'undefined'.\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"如果不需要索引,则可以使用for–of循环或forEach调用来遍历各个元素。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"function screamLines(strs: string[]) {\n \/\/ this works fine\n for (const str of strs) {\n console.log(str.toUpperCase());\n }\n\n\n \/\/ this works fine\n strs.forEach(str => {\n console.log(str.toUpperCase());\n });\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"捕获越界错误时这个标志可能很方便,但它对于很多代码来说可能显得很累赘,因此--strict标志不会自动启用它。但如果你对这个特性很感兴趣,也可以随意尝试它,看它是否适合你团队的代码库!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"欲了解更多信息,请查看实现的"},{"type":"link","attrs":{"href":"https:\/\/github.com\/microsoft\/TypeScript\/pull\/39560","title":"xxx","type":null},"content":[{"type":"text","text":"拉取请求"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"。"}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"没有baseUrl的paths"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"路径映射是相当常用的,通常是为了更好地导入,或者为了模拟monorepo链接行为。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"不幸的是,指定paths来启用路径映射时,还需要指定一个名为baseUrl的选项,该选项也允许到达相对于baseUrl的bare specifier paths。它还经常会使自动导入使用较差的路径。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"在TypeScript 4.1中,可以在没有baseUrl的情况下使用path选项,从而避免其中一些问题。"}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"checkJs隐含allowJs"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"以前,如果你要启动一个checked的JavaScript项目,则必须同时设置allowJs和checkJs。这有点烦人,因此现在checkJs默认隐含了allowJs。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"欲了解更多信息,请查看"},{"type":"link","attrs":{"href":"https:\/\/github.com\/microsoft\/TypeScript\/pull\/40275","title":"xxx","type":null},"content":[{"type":"text","text":"拉取请求"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"。"}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"React 17 JSX工厂"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"TypeScript 4.1通过jsx编译器选项的两个新选项,支持了React 17即将推出的jsx和jsxs工厂函数:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"react-jsx"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"react-jsxdev"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"这些选项分别用于生产和开发编译环境。一般来说,一个选项可以从另一个扩展而来。例如,用于生产构建的tsconfig.json可能如下所示:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"\/\/ .\/src\/tsconfig.json\n{\n \"compilerOptions\": {\n \"module\": \"esnext\",\n \"target\": \"es2015\",\n \"jsx\": \"react-jsx\",\n \"strict\": true\n },\n \"include\": [\n \".\/**\/*\"\n ]\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"用于开发的构建可能如下所示:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"\/\/ .\/src\/tsconfig.dev.json\n{\n \"extends\": \".\/tsconfig.json\",\n \"compilerOptions\": {\n \"jsx\": \"react-jsxdev\"\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"欲了解更多信息,请查看相应的"},{"type":"link","attrs":{"href":"https:\/\/github.com\/microsoft\/TypeScript\/pull\/39199","title":"xxx","type":null},"content":[{"type":"text","text":"PR"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"。"}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"JSDoc @see标签的编辑器支持"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"JSDoc @see标签现在在TypeScript和JavaScript的编辑器中得到了更好的支持。这样你就可以在标签后的虚线名称中使用go-to-definition之类的功能。例如,在下面的示例中,仅对JSDoc注释中的first或C进行go-to-defintion即可:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"\/\/ @filename: first.ts\nexport class C { }\n\n\n\/\/ @filename: main.ts\nimport * as first from '.\/first';\n\n\n\/**\n * @see first.C\n *\/\nfunction related() { "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"感谢积极贡献者 Wenlu Wang 实现它!"}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"重大更改"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"abstract成员不能被标记为async"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"标记为abstract的成员不能再标记为async。此处的解决方法是移除async关键字,因为调用方只关心返回类型。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"any\/unknown在falsy位置传播"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"以前,对于像foo && somethingElse这样的表达式,foo的类型是any或unknown的,整个表达式的类型将是somethingElse的类型。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"例如,以前在下列代码中x的类型为{ someProp: string }。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"declare let foo: unknown;\ndeclare let somethingElse: { someProp: string };\n\n\nlet x = foo && somethingElse;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"但在TypeScript 4.1中,我们会更谨慎地确定这种类型。由于对&&左侧的类型一无所知,因此我们将向外传播any和unknown,而不是将右侧的类型传播出去。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"它最常见的使用模式出现在检查booleans的兼容性时,尤其是在谓词函数中。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"function isThing(x: any): boolean {\n return x && typeof x === 'object' && x.blah === 'foo';\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"一般来说,合适的解决方法是从foo && someExpression切换到!!foo && someExpression。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"条件spread创建可选属性"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"在JavaScript中,对象spread(例如{ ...foo })不会对虚假值起作用。因此,在类似{ ...foo }的代码中,如果foo为null或undefined,则会跳过foo。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"许多用户利用此优势“有条件地”在属性中spread。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"interface Person {\n name: string;\n age: number;\n location: string;\n}\n\n\ninterface Animal {\n name: string;\n owner: Person;\n}\n\n\nfunction copyOwner(pet?: Animal) {\n return {\n ...(pet && pet.owner),\n otherStuff: 123\n }\n}\n\n\n\/\/ We could also use optional chaining here:\n\n\nfunction copyOwner(pet?: Animal) {\n return {\n ...(pet?.owner),\n otherStuff: 123\n "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"在这里,如果定义了pet,则pet.owner的属性将被spread进去;否则,不会将任何属性spread到返回的对象中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"copyOwner的返回类型以前是基于每个spread的联合类型:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"{ x: number } | { x: number, name: string, age: number, location: string }"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"这个操作是这样的:如果定义了pet,Person的所有属性都将存在;否则,所有属性都不会在结果上定义。要么全有,要么都没有。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"但有人把这种模式用得太过分了,在单个对象中塞几百个spread,每个spread都可能添加数百或数千个属性。事实证明,由于各种原因,这种做法的成本最后会飞天,并且往往不会带来太多收益。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"在TypeScript 4.1中,返回的类型改为使用all-optional属性。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"{\n x: number;\n name?: string;\n age?: number;\n location?: string;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"这样性能和代码简洁程度都会上一个台阶。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"欲了解更多信息,请参见原始更改("},{"type":"link","attrs":{"href":"https:\/\/github.com\/microsoft\/TypeScript\/pull\/40778","title":null,"type":null},"content":[{"type":"text","text":"https:\/\/github.com\/microsoft\/TypeScript\/pull\/40778"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":")。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"--declaration和--outFile需要包名称根"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"当你有一个同时使用outFile和declaration,来为你的项目发出单个.js文件以及相应的.d.ts文件的项目时,该声明文件通常需要对模块标识符进行某种后处理,才能对外部消费者有意义。例如,像这样的项目:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"\/\/ @filename: projectRoot\/index.ts\nexport * from \".\/nested\/base\";\n\n\n\/\/ @filename: projectRoot\/nested\/base.ts\nexport const a = \"123\""}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"将生成一个如下所示的.d.ts文件:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"declare module \"nested\/base\" {\n export const a = \"123\";\n}\ndeclare module \"index\" {\n export * from \"nested\/base\";\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"从技术上讲这是准确的,但没那么有用。当请求生成单个.d.ts文件时,TypeScript 4.1会要求指定bundledPackageName。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"declare module \"hello\/nested\/base\" {\n export const a = \"123\";\n}\ndeclare module \"hello\" {\n export * from \"hello\/nested\/base\";\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"没有这个选项的话,你可能会收到像下面这样的错误消息:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"The `bundledPackageName` option must be provided when using outFile and node module resolution with declaration emit."}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"在 Promise中,resolve的参数不再可选"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"在编写如下代码时:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"new Promise(resolve => {\n doSomethingAsync(() => {\n doSomething();\n resolve();\n })\n})"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"你可能会收到这样的错误:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":" resolve()\n ~~~~~~~~~\nerror TS2554: Expected 1 arguments, but got 0.\n An argument for 'value' was not provided."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"这是因为resolve不再具有可选参数,因此默认情况下现在必须为它传递一个值。一般来说,使用Promise时这样会捕获合法错误。典型的解决方法是为其传递正确的参数,有时还要添加一个显式的类型参数。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"new Promise(resolve => {\n \/\/ ^^^^^^^^\n doSomethingAsync(value => {\n doSomething();\n resolve(value);\n \/\/ ^^^^^\n })\n})"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"但有时确实需要在没有参数的情况下调用resolve()。在这些情况下,我们可以给Promise一个显式的void泛型类型参数(即将其写为Promise)。这利用了TypeScript 4.1中的新功能,其中可能是void的尾随参数可以变为可选。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"new Promise(resolve => {\n \/\/ ^^^^^^\n doSomethingAsync(() => {\n doSomething();\n resolve();\n })\n})"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"TypeScript 4.1附带了一个快速修复以帮助解决这个问题。"}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"下一步计划"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"在接下来的几周内,我们将密切注意TypeScript 4.1的稳定版本中需要包含的所有高优先级修复。如果可以的话,请试试我们的RC版本,帮助我们找出各种潜在问题。我们一直在努力改善大家的TypeScript体验!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"编程快乐!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"原文链接:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/devblogs.microsoft.com\/typescript\/announcing-typescript-4-1-rc\/#breaking-changes","title":null,"type":null},"content":[{"type":"text","text":"https:\/\/devblogs.microsoft.com\/typescript\/announcing-typescript-4-1-rc\/#breaking-changes"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章