TS 设置排除相同子类型的多个对象

问题描述 投票:0回答:1

我有一个抽象类

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

正确的声明是什么?

typescript set typing
1个回答
0
投票
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 评论的代码模板)

© www.soinside.com 2019 - 2024. All rights reserved.