我有一个抽象类
Spell
带有用于具体实例化的子类。
abstract class Spell {
name: string
manaCost: number
constructor(name: string, manaCost: number) {
this.name = name
this.manaCost = manaCost
}
}
class Fireball extends Spell { ... }
class Lightning extends Spell { ... }
我有一个带有法术列表的
Character
class Character {
spells: Spell[]
addSpell(spell: Spell) {
this.spells.push(spell)
}
}
我想防止将多个相同类型的法术实例添加到角色的
.spells
数组中,即只能添加一个FireballSpell
实例。我尝试使用 Set
而不是数组
class Character {
spells: Set<Spell>
addSpell(spell: Spell) {
this.spells.add(spell)
}
}
但是,因为法术是不同的实例,所以它们不被认为是重复的,并且允许添加。
const character = new Character()
character.addSpell(new Fireball())
character.addSpell(new Fireball())
console.log(character.spells.size) // 2, should be 1
正确的声明是什么?
type SpellName = 'Fireball' | 'Lightning' // or enum
// spell name is generic, it freezes constructor arg
// You may limit it to `extends SpellName` if you want
abstract class Spell<Name = string> {
name: Name
manaCost: number
constructor(name: Name, manaCost: number) {
this.name = name
this.manaCost = manaCost
}
}
class Fireball extends Spell<'Fireball'> {
constructor() {
super("Fireball", 123);
}
}
class Lightning extends Spell<'Lightning'> {
constructor() {
super("Lightning", 234);
}
}
// Often a map of those is made, this forces every SpellName to be used
const spellMap: {[K in SpellName]: new () => Spell<K>} = {
Fireball,
Lightning
}
class Character {
spellsMap: Map<string, Spell> = new Map();
spells: Spell[] = [];
addByName(spell: SpellName) {
return this.addSpell(new spellMap[spell]())
}
addSpell(spell: Spell) {
// override spell in the Map<string, Spell>
this.spellsMap.set(spell.name, spell);
// add spell to Array<Spell> if missing
if (!this.spells.find(s => s.name === spell.name)) {
// ^ could've use `![].every` or `[].some` but `find` may be more straightforward for beginners
this.spells.push(spell)
}
// well, the Set<Spell> would be same as Array<Spell>
console.log('spells: ', this.spells, this.spellsMap)
}
}
const character = new Character()
character.addSpell(new Fireball()) // spells: [Fireball] Map(1){"Fireball"=>Fireball}
character.addSpell(new Fireball()) // spells: [Fireball] Map(1){"Fireball"=>Fireball}
character.addSpell(new Lightning()) // spells: [Fireball,Lightning] Map(1){"Fireball"=>Fireball,"Lightning"=>Lightning}
(来自 jcalz 评论的代码模板)