0

I'm trying to set dynamically a property to a class in TypeScript:

class Foo {
    a: number;
    b: number;
    c: string;
    d: AnotherType
    e: Function;

    constructor(a: number, b: number) {
        this.a = a;
        this.b = b;
        //...
    }

    get sum(): number {
        return this.a + this.b;
    }

    get name(): string {
        return `My name is ${this.c}`;
    }

    public updateProperty(
        prop: keyof Foo,
        value: number,
    ) {
        this[prop] = value;
    }
}

But I'm getting two errors:

  • TS2540: Cannot assign to 'sum' because it is a read-only property.
  • TS2322: Type 'number' is not assignable to type 'number & ((prop: keyof Foo, value: number) => void)'.
    Type 'number' is not assignable to type '(prop: keyof Foo, value: number) => void'.

I understand that the first error is because I cannot redefine sum, and the second error is because updateProperty is a function, so I shouldn't redefine it.

Is there a way that I can create a type for prop that resolves to: "all the class members that aren't getters or instance methods"?

This answer talks about a property that has a type, but in this example, Foo only has number, and it should work, but if won't work if a is a string, also it won't fix the getter case.

This answer tries to apply a snapshot of a partial record to this, but that will cause a circular dependency and will fail

IAmJulianAcosta
  • 1,082
  • 2
  • 14
  • 30
  • Ehh, there's a lot working against you here. You could jump through a lot of type hoops like [this](//stackoverflow.com/a/52473108/2887218) to get the writable keys of a type, and [this](//stackoverflow.com/a/56874389/2887218) to get the keys whose properties are a supertype of `number`, and combine them to take an existing type and get the keys to which you can write `number`... sort of like [this](//tsplay.dev/NBeV4w). But if you try to use that *inside* the definition of `Foo` itself it will have a circularity warning, so you have to [omit that method name](//tsplay.dev/mbGaEW). – jcalz Nov 29 '21 at 02:10
  • 2
    So much craziness just to avoid writing out `"a" | "b"` manually seems not worth it to me. I can write up an answer anyway, if there's nothing else about this that I'm missing. – jcalz Nov 29 '21 at 02:11
  • Does this answer your question? [How to exclude getter only properties from type in typescript](https://stackoverflow.com/questions/52443276/how-to-exclude-getter-only-properties-from-type-in-typescript) – Jared Smith Nov 29 '21 at 02:20
  • @jcalz I have a class with "a" | "b" ... "m" | "n", trying to avoid that – IAmJulianAcosta Nov 29 '21 at 02:25
  • I get it, but you can do `Exclude` or something. But as I said, I can write up an answer which [painstakingly finds those keys of `Foo` to which you can write a `number` value while avoiding the circularity warning](https://tsplay.dev/mbGaEW), if you want. *Do* you want that? Or is there something else I'm missing? – jcalz Nov 29 '21 at 02:29
  • @jcalz the class that I'm working on has several properties (with different types) and methods, so I'm trying to avoid hardcoding as much as possible, so I'd need to have any keys that are writable (not method, not getter). I edited the answer to reflect the complexity of the class – IAmJulianAcosta Nov 29 '21 at 02:34
  • 1
    Other than the fact that your new example has unrelated errors, [my solution still looks like it works](https://tsplay.dev/m3aEEw). I'm going to ask one more time and then I guess I'll stop so as not to belabor the point: does this solution work for your needs? If so, I will write it up as an answer and post that. If not, what specifically does not work for you? – jcalz Nov 29 '21 at 02:42
  • If you are thinking that it doesn't work because I'm requiring it to be `number`, that's what you are doing by making `updateProperty()` take a `value` parameter of type `number`. If something else should be happening there, it would behoove you to make your code a [mre] of what you're trying to do. If `value` is not `number`, what is it? – jcalz Nov 29 '21 at 02:44
  • If [this](https://tsplay.dev/wX2l1m) is what you're looking for, could you edit your code example to conform to it somewhat? If I'm still missing something, let me know. Crossing my fingers here. – jcalz Nov 29 '21 at 02:50

0 Answers0