有没有一种优雅的方法可以只设置一次 class 属性 ?

Is there an elegant way to set a class property only once?

在 TypeScript 中,而在 class 中,有没有一种优雅的方法可以只设置一次值?换句话说,有没有等价于赋值后设置为readonly

例如:

class FooExample {
    public fixedValue: string;

    public setFixedValue(value: string): void {
        if (!this.fixedValue) {
            // this value should be immutable
            this.fixedValue = value;
        }
    }
}

我不是在寻找 getter,因为 class 属性 可以在 class 本身内更改。

您可以使用“智能”setter 来实现这一点。这个想法是在 class 上定义一个 setter,从其内部删除 setter 并通过调用的 Object.defineProperty 方法定义一个只读的 属性 writable 属性 设置为 false.

的描述符

“在 class 本身内发生变化”部分可以通过另一个 属性 重新定义来实现。不要忘记首先制作 属性 configurable

这是完整的样子(我认为没有更优雅的方法来做到这一点)- 答案的运行时版本不是特定于 TypeScript 的:

class ReadonlyFields {
  set fixed(value) {
    Object.defineProperty(this, "fixed", {
      value,
      writable: false,
      configurable: true,
    });
  }

  doSomething() {
    Object.defineProperty(this, "fixed", {
      value: 24, //exact logic for changing the value is up to you
      writable: false
    });
  }
}

const r = new ReadonlyFields();
r.fixed = 42;
console.log(r.fixed); //42;
r.fixed = 24;
console.log(r.fixed); //42;
r.doSomething();
console.log(r.fixed); //24;

如果您还想从类型系统的角度强制执行此操作,只需将 属性 readonly:

class ReadonlyFields {
  readonly fixed!: number;

  setFixed(value: number) {
    Object.defineProperty(this, "fixed", {
      value,
      writable: false,
      configurable: true,
    });
  }

  doSomething() {
    Object.defineProperty(this, "fixed", {
      value: 24, //exact logic for changing the value is up to you
      writable: false,
    });
  }
}

const r = new ReadonlyFields();
r.setFixed(42); //OK
//@ts-expect-error
r.fixed = 24;
r.doSomething(); //OK

在阅读了建议的答案并进行了自己的研究之后,我相信我已经找到了最优雅的解决方案。您首先将值定义为 Object,然后在填充相关的 属性.

之后将 freeze() 定义为 Object
class FooExample {
    public fixedValue = {
        value: '';
    };

    public setFixedValue(value: string): void {
        if (!Object.isFrozen(this.fixedValue)) {
            this.fixedValue.value = value;
            Object.freeze(this.fixedValue);
        }
    }
}