网站搜索

javascript - 在 Typescript 中更新类的动态键


我有一个类,例如:

class Team {
  pointsFor: number;
  pointsAgainst: number;
  
  constructor(){
    this.pointsFor = 0;
    this.pointsAgainst = 0;
  }
}

现在我想为这个类创建一个方法,通过键来更新属性,例如:

updateStats = (bool: boolean, property: string, increment: number) =>{
  const key: keyof this = bool ? `${property}For` : `${property}Against`
  this[key] += increment
}

但是我收到一个错误,提示字符串不能赋值给 this 的 keyof,如果我强制类型的话

const key: keyof this = bool ? `${property}For` as keyof this : `${property}Against` as keyof this

然后我得到一个错误:"运算符 '+=' 不能应用于类型 'this[keyof this]' 和 'number'。"

有没有一种方法可以实现我在这里所想要的,即调用 team.updateStats(true, 'points', 2) 并更新 pointsFor?

答案#1

我真的不建议以你正在尝试的方式对 TypeScript 类属性进行字符串操作。

通过使用 keyof this,你本质上是在引用类的所有键,比如 pointsFor: number、pointsAgainst: number 和 updateStats: Function。如你所见,并非所有的键都是数字。

另一个问题是 string 可以是任何东西,所以如果 TypeScript 不阻止你,updateStats(true, 'blah blah', 3) 可能会变得有效。

最好将你需要的特定属性限制在类中具有明确定义属性的对象中。

例如:

class Team {
  teamProps: { [key: string]: { for: number, against: number }} = {
    points: { for: 0, against: 0 }
  }

  updateStats = (bool: boolean, property: keyof typeof this.teamProps, increment: number) =>{
    this.teamProps[property][bool ? 'for' : 'against'] += increment;
  }
}

你可以通过 <Team>.teamProps.points.for 来访问这些属性,而不是 <Team>.pointsFor。

答案#2

你几乎可以用 TypeScript 的类型系统做所有事情:

TeamProp 取一个键的联合(在本例中为 keyof this)并只返回匹配 $ {string}For 或 $ {string}Against 的字符串。

type TeamProp<K> = K extends `${infer P}For` ? P : K extends `${infer P}Against` ? P : never;

class Team {
    pointsFor: number;
    pointsAgainst: number;

    constructor() {
        this.pointsFor = 0;
        this.pointsAgainst = 0;
    }

    updateStats<P extends TeamProp<keyof this>>(bool: boolean, property: P, increment: number) {
        const key = bool ? `${property}For` : `${property}Against`
        this[key] += increment
    }
}

const t = new Team()

// Works
t.updateStats(true, 'points', 1);

// Argument of type '"foo"' is not assignable to parameter of type '"points"'.
t.updateStats(true, 'foo', 1);