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主页)
希望你今天学到点新东西!✌️