继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

别这么用 TypeScript 类型!试试 Map 模式吧

慕尼黑5688855
关注TA
已关注
手记 240
粉丝 8
获赞 17

https://dev.to/perisicnikola37/dont-use-typescript-types-like-this-use-map-pattern-instead-ki3 "不要这样使用TypeScript类型,试试使用Map模式(详情见链接)"

介绍

在实际项目中工作时,我遇到了一个特别的TypeScript实现,它是功能性的但不够灵活。在这篇文章里,我将讲述我遇到的问题,以及我如何通过使用Map模式使设计更加灵活。

这个问题

我碰到了这种 TypeScript 类型定义。

    // FinalResponse.ts
    import { Reaction } from './Reaction'

    export type 最终响应 = {
      总分: number
      标题罚分: number
      句子罚分: number
      字符罚分: number
      单词罚分: number
      标题: string[]
      句子: string[]
      单词: string[]
      链接: { href: string; text: string }[]
      超出: {
        超出句子: string[]
        重复的单词: { 单词: string; 数量: number }[]
      }
      反应: {
        likes: Reaction
        unicorns: Reaction
        explodingHeads: Reaction
        raisedHands: Reaction
        fire: Reaction
      }
    }

此外,还定义了这种 Reaction(反应)类型。

    // Reaction.ts
    export type Reaction = {  
      count: number  
      percentage: number  
    }

它就是这样被用在一个函数里。

    // calculator.ts
    export const calculateScore = (  
      headings: string[],  
      sentences: string[],  
      words: string[],  
      totalPostCharactersCount: number,  
      links: { href: string; text: string }[],  
      reactions: {  
        likes: Reaction,  
        unicorns: Reaction,  
        explodingHeads: Reaction,  
        raisedHands: Reaction,  
        fire: Reaction,  
      },  
    ): FinalResponse => {  
      // 计算分数的逻辑...
    }
这种方法的问题在于

现在,想象一下开发者需要添加一个新的反应,比如爱心或鼓掌。鉴于当前的设置,他们需要做以下几件事:

  • 在 ReactionType 枚举中添加一个新的枚举值。
  • 更新数据库迁移文件,添加新的反应类型。
  • 更新前端代码,以便显示新的反应图标。
  • 更新后端代码,以处理新的反应类型。

  • 修改 FinalResponse.ts 文件以添加新的反应类型。
  • 如有必要,请更新 Reaction.ts 类型。
  • 修改 calculateScore 函数,使其包含新的反应。
  • 可能还需更新应用程序中依赖此结构的其他部分。

所以他们不是只在一个地方添加新的反应,而是结果在三个或更多的文件中进行修改,这增加了错误和冗余的可能性。这种方法耦合得很紧。

解决办法(待定)

我通过引入一个更灵活和可重用的结构,想出更简洁的解决方案。

// 最终响应文件,定义了评分和惩罚规则
// FinalResponse.ts
import { Reaction } from './Reaction'

export type ReactionMap = Record<string, Reaction>

export type FinalResponse = {
  totalScore: number,
  headingsPenalty: number,
  sentencesPenalty: number,
  charactersPenalty: number,
  wordsPenalty: number,
  headings: string[],
  sentences: string[],
  words: string[],
  links: { href: string; text: string }[],
  exceeded: {
    exceededSentences: string[],
    repeatedWords: { word: string; count: number }[],
  },
  reactions: ReactionMap,
}

无具体内容需要解释。

  • ReactionMap: 这种类型使用 Record<string, Reaction> 形式,表示任何字符串都可以作为键,而值的类型始终为 Reaction
  • FinalResponse: 现在,FinalResponse 中的 reactions 字段的类型为 ReactionMap,这意味着你可以根据需要动态添加任何反应而无需更改其他文件。
清洁代码

calculator.ts 文件中,该函数现在是这样的:

    // calculator.ts
    export const calculateScore = (  
      headings: string[],  
      sentences: string[],  
      words: string[],  
      totalPostCharactersCount: number,  
      links: { href: string; text: string }[],  
      reactions: ReactionMap,  
    ): FinalResponse => {  
      // 分数计算...
    }
瑞典有一说:但是等等!我们需要一点控制

尽管新的解决方案提供了灵活性,但也引入了添加未经审核的反应的风险,这意味着任何人都可以随时可能添加任何字符串内容作为反应。我们肯定不希望看到这种情况。

为解决这个问题,我们可以更严格地控制允许反应。

更安全的解决办法

这里是我们将反馈限制在一组允许值内的更新版本。

    // FinalResponse.ts
    import { Reaction } from './Reaction'

    type AllowedReactions =
      | 'likes'
      | 'unicorns'
      | 'explodingHeads'
      | 'raisedHands'
      | 'fire'

    export type ReactionMap = {
      [key in AllowedReactions]: Reaction
    }

    export type FinalResponse = {
      totalScore: number
      headingsPenalty: number
      sentencesPenalty: number
      charactersPenalty: number
      wordsPenalty: number
      headings: string[]
      sentences: string[]
      words: string[]
      links: { href: string; text: string }[]
      exceeded: {
        exceededSentences: string[]
        repeatedWords: { word: string; count: number }[]
      }
      reactions: ReactionMap
    }

视觉表现

结论

这种方法兼顾了灵活性和控制:

  • 灵活性强:你可以通过修改 AllowedReactions 类型轻松地添加新的反应。
  • 控制:使用联合类型确保只能使用允许的反应,避免出现或添加无效或不需要的反应。

这段代码符合开闭原则(OCP),通过扩展来增加新功能,无需修改现有代码。

使用这种模式,我们可以轻松地扩展反应种类列表而不会修改太多文件,同时也能严格控制可以添加的新内容。

代码?

你可以在这里访问这个仓库[这里](https://github.com/perisicnikola37/dev-to-rater)\。

别的故事

找我聊聊

GitHub (GitHub主页), LinkedIn (领英主页), dev.to (dev.to主页)

希望你今天学到点新东西!✌️

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP