• 如何用「Map模式」更灵活地定义TypeScript类型
  • 发布于 1个月前
  • 235 热度
    0 评论
TypeScript以其强大的类型系统深受开发者喜爱,但有时候我们在项目中定义类型的方式会导致代码缺乏灵活性和扩展性。今天我就用一个实际案例,带大家看看如何用「Map模式」更灵活地定义TypeScript类型。

一、遇到的问题
项目中,我发现团队成员使用了如下的类型定义方式:
// FinalResponse.ts
import { Reaction } from'./Reaction'

exporttypeFinalResponse = {
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: {
    likes: Reaction
    unicorns: Reaction
    explodingHeads: Reaction
    raisedHands: Reaction
    fire: Reaction
  }
}
上面的类型定义看似清晰,但如果现在我想新增一个「爱心」的reaction,意味着我要去修改多个地方:
• 修改 FinalResponse 类型;
• 更新相关的函数实现,比如 calculateScore。
这种做法不仅麻烦,而且容易出错。

二、如何用Map模式优化?
我们可以引入一个「Map模式」,让reaction的定义更灵活,更易于扩展。
// Reaction.ts
exporttypeReaction = {
count: number
percentage: number
}
// 改进后的FinalResponse.ts
exporttypeReactionMap = Record<string, Reaction>

exporttypeFinalResponse = {
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
}
为什么这样更好?
• 扩展性强:新增reaction时不再需要反复修改多个文件,只要新增一个键值即可。

• 降低耦合:代码变动集中在更小范围内,便于维护。


三、进一步优化:如何避免反应失控?
上面的实现虽好,但也存在风险:Reaction的键是任意字符串,意味着容易引入不必要的值。因此,我们可以用更严格的联合类型来限制允许的reaction类型。
// 堆代码 duidaima.com
// 优化后的FinalResponse.ts
import { Reaction } from'./Reaction'
typeAllowedReactions =
  | 'likes'
  | 'unicorns'
  | 'explodingHeads'
  | 'raisedHands'
  | 'fire'

exporttypeReactionMap = {
  [key inAllowedReactions]: Reaction
}

exporttypeFinalResponse = {
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
}
这样做的好处就是我们不仅保留了原先的灵活性,还能严格控制reaction的类型,避免随意添加未知的类型,确保了代码的安全性。

三、实际应用示例
使用新定义的类型后,我们的calculateScore函数变得更简洁了:
// calculator.ts
exportconst calculateScore = (
headings: string[],
sentences: string[],
words: string[],
totalPostCharactersCount: number,
links: { href: string; text: string }[],
reactions: ReactionMap,
): FinalResponse => {
// 计算逻辑放在这里...
}
三、总结一下
这样做最大的好处是:
• 开放/封闭原则(OCP):易于扩展新功能,不用频繁改动已有代码。
• 代码更干净:结构清晰,降低维护难度。

用户评论