別這么用 TypeScript 類型!試試 Map 模式吧
简介
在实际项目中,我发现了一个特定的TypeScript实现,它虽然功能上是有效的但缺乏灵活性。在这篇博客中,我将向您介绍我遇到的问题,以及我是如何通过使用Map模式来使设计更加动态,从而改进了设计。
目录:- 问题点
- 这种做法的问题
- 解决办法
- 整洁的代码
- 更安全的解决办法
- 可视化
- 总结
……
问题我发现了一个这样的 TypeScript 类型:
// 最终响应.ts
import { 反馈 } from './Reaction'
export type 最终响应 = {
totalScore: number
headingsPenalty: number
sentencesPenalty: number
charactersPenalty: number
wordsPenalty: number
headings: string[]
sentences: string[]
words: string[]
links: { href: string; text: string }[]
超出部分: {
exceededSentences: string[]
repeatedWords: { word: string; count: number }[]
}
反馈: {
likes: 反馈
unicorns: 反馈
爆炸头: 反馈
举手: 反馈
火焰反应: 反馈
}
}
进入全屏模式 退出 全屏
此外,还定义了这种反应类型 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 => {
// 计算分数的逻辑...
}
点击进入全屏,退出全屏
这种方法的问题在于...
现在来想象开发者需要添加一个新的反应(例如,爱心图标、鼓掌图标等)的情况。在这种情况下,他们需要:
- 修改
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
:任何字符串都可以作为键,值始终为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 => {
// 计算分数的逻辑...
}
全屏进入 / 全屏退出
但是等等!我们还需要一些控制。
虽然新的解决方案带来了灵活性,但也增加了添加未检查反应的风险,这意味着任何人都可以随意添加任何字符串作为反应。我们肯定不希望出现这种情况。
为了纠正这个问题,我们可以加强对允许反应的控制。
更安全的解决办法
这是更新后的版本,我们将反应限制在一组预设的允许值范围内。
// 最终响应.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),通过扩展添加新功能,而无需修改现有代码。
通过这种模式,我们可以轻松扩展反应列表而不必修改太多的文件,同时仍然保持严格的控制,确保可以添加的内容符合要求。
代码?啥?
你可以在这里查看仓库:here
希望这个解决方案对你有所帮助!感谢您的阅读。😊
*[面向对象(OO)]
共同學習,寫下你的評論
評論加載中...
作者其他優(yōu)質文章