最近我看到 Jamie Kyle 关于使用解构、默认参数和内联类型的推文:
我最近在日常工作中看到的那条推文和一些 React 组件启发了我写这篇博文。我想向你展示使用解构和内联类型如何降低 TypeScript 的可读性!
在 JavaScript 和 TypeScript 中,您可以使用function
关键字或 lambda/arrow 函数来定义函数。两种方式都有效,但也有区别。让我们看一下简单的sendMessage
函数。实现逻辑与我们无关。
// sendMessage function written using `function` keyword function sendMessage(message: string) { // function logic } // same sendMessage written as arrow function const sendMessage = (message: string) => { // function logic };
当函数定义非常简单时,函数接受一些不同类型的参数。如果它们是字符串或数字之类的原语,那么一切都是可读的。
假设您想将一些附加信息与您的消息内容一起传递给sendMessage
函数。
function sendMessage(message: { content: string; senderId: string; replyTo?: string; }) { // you can assess content using `message.content` here }
如您所见,TypeScript 允许您为要传递的message
对象编写内联类型定义,而无需使用type
或interface
关键字指定类型。
让我们添加解构。当您将大型message
对象传递给您的函数时,TypeScript 允许拆分传递的参数以减少多次重复message
变量的代码样板。
function sendMessage({ content, senderId, replyTo, }: { content: string; senderId: string; replyTo?: string; }) { // you have access to `content` directly }
这可能看起来是个好主意,毕竟你不需要写那么多次message
,对吧?事实证明这并不是那么好。让我们谈谈我认为它是反模式的 5 个原因。
当您阅读函数体时,您会看到senderId
您必须仔细检查以确保该函数来自何处。它是作为参数传递还是在函数的某处计算?
当所有类型都被函数定义中的解构所束缚时,没有自然的地方可以编写文档注释。您可以在每个类型字段之间写注释,但这会使整个函数定义更长。它积极地阻止您编写您传递的数据的快速摘要。
当您的数据被解构时,如果您想将其向前传递,则需要再次将其结构化为一个新对象。这不鼓励创建较小的辅助函数并依赖组合来构建您的主函数逻辑。
如果在编写主函数逻辑时需要在辅助函数中重用函数参数,则必须重复键入同一组类型。这使得根本不编写类型变得更容易。
面对现实吧。这只是占用大量屏幕空间的大量代码行。此外,它侧重于实现细节——您传递给函数的参数的内部类型,这在您查看该函数时大部分时间并不相关。
提取类型并将其放在函数的正上方使其更具可读性。有一个文档注释的地方,您可以在其他一些辅助函数中重用该类型,并在需要时在一个地方更改类型定义。
/** * Message to send using XYZ API */ export type MessageToSend = { /** * Markdown string of the user's message */ content: string; /** * Id of the sender user */ senderId: string; /** * Other message ID if this is a reply */ replyTo?: string; }; function sendMessage(message: MessageToSend) { // function logic } function getUserIdsToNotify(message: MessaageToSend) { // function logic }
查找我在研究此博客文章时使用的资源列表: