1

I have this object model:

export class FrcCapacity {
  constructor(
    public id?: number,
    public frcId?: number,
    public capGroupId?: number,
    public capGroup?: CapGroup,
    public salesProductId?: number,
    public p1?: number,
    public p2?: number,
    public p3?: number,
    public p4?: number,
    public p5?: number,
    public p6?: number,
    public p7?: number,
    public p8?: number,
    public p9?: number,
    public p10?: number,
    public p11?: number,
    public p12?: number,
    public total?: number
  ) {}
}

And I have an array of this, f. e. frcCapacity, filled with objects of the above object model.

I want to write a function, where I want to set the px value of the processed object. For this, I have all needed data and the function body looks like this:

periodValueChange(val: string, rowIndex: number, field: string) {
    for (let [key, value] of Object.entries(this.frcCapacity[rowIndex])) {
      if (key === field) this.frcCapacity[rowIndex]???
    }
  }

I'm trying this with Object.entries, but what should I write in place of the ???. How can I access the px field based on the field string parameter?

After some thinking and searching, this solution works:

periodValueChange(val: string, rowIndex: number, field: string) {
    let frcCap = this.frcCapacity[rowIndex];
    let map = new Map(Object.entries(frcCap));
    for (let [key, value] of map) {
      if (key === field) {
        map.set(field, +val);
      }
    }
    let obj = Array.from(map).reduce(
      (obj, [key, value]) => Object.assign(obj, { [key]: value }),
      {}
    );
    this.frcCapacity[rowIndex] = obj;
  }

Basically, I needed something like this:

periodValueChange(val: string, rowIndex: number, field: string) {
    this.frcCapacity[rowIndex].field = +val;
  }

Where the field parameter can be p1, p2, etc.

6
  • @Andreas - This is TypeScript, not JavaScript, which is relevant here (not always, but in this case). Still, there must be a TypeScript version of that... Commented Jan 27, 2022 at 8:25
  • You have val as string, but none of FrcCapacity's properties have the type string. Most of them are number, and one of them is CapGroup. So why does this take string? Commented Jan 27, 2022 at 8:29
  • How to dynamically access object property in TypeScript Commented Jan 27, 2022 at 8:31
  • @Andreas Still doesn't handle the issue of the property type, that's just keys. There must be one (I even think I've seen it, related to setting object properties based on form fields), but I'm not having any luck finding it. Commented Jan 27, 2022 at 8:33
  • The solution you've posted is much, much more complex than needed, and has the problem that the resulting object doesn't inherit from FrcCapacity.prototype. The reason it works is the loose typing around Object.entries and the untyped map. You might as well just do (this.frcCapacity[rowIndex] as any)[field] = +val; if you're not worried about type safety. Commented Jan 27, 2022 at 9:58

1 Answer 1

1

Since you've used string for both the property name (field) and the value (val), I'm going to assume that you're getting those strings from somewhere that you can't get non-strings from (like form fields). So there are two challenges:

  1. field could be an invalid property name for FrcCapacity objects, and

  2. val may not be a valid value for whatever property field identifies.

To handle this in a mostly-typesafe way, you'll need a function that validates that a key is a valid FrcCapacity key, such as a type assertion function:

function assertIsValidFrcCapacityKey(key: string): asserts key is keyof FrcCapacity {
    switch (key) {
        case "id":
        case "frcId":
        case "capGroupId":
        // ...and so on for all the valid property names...
            break;
        default:
            throw new Error(`${key} is not a valid key for FrcCapacity objects`);
    }
}

That tells TypeScript that if that function doesn't throw, the key passed in is a valid key for FrcCapacity objects. Obviously, it's less than ideal that you have to repeat all the property names, but there we are.

Then you have to handle the number / CapGroup thing, which you can hardcode into the function:

periodValueChange(val: string, rowIndex: number, field: string) {
    assertIsValidFrcCapacityKey(field);
    if (field === "capGroup") {
        frcCapacity[rowIndex].capGroup = convertStringToCapGroup(val);
    } else {
        frcCapacity[rowIndex][field] = +val;
    }
}

(In that example I've used unary + to convert the val string to a number, but there are other ways; I list the various options and their pros and cons here. I've also left the implementation of convertStringToCapGroup to you, since I don't know what a CapGroup is, nor how you're encoding it as a string in your form.)

Sign up to request clarification or add additional context in comments.

7 Comments

Why there is a community wiki ? What does it mean?
I feel certain there's a good dupetarget for this, but couldn't find one, so ultimately just ended up posting an answer. If anyone does find one, please @ ping me so I can remove it (and add that question to my list of good dupetargets for TypeScript).
@captain-yossarian - LOL, overlapping comments -- see my comment just above. I mark answers Community Wiki when I want to avoid any impression that I'm answering just to get rep (which sadly many people who post answers to duplicates do). CW answers don't earn rep. I can't imagine this isn't a duplicate question, but I can't find a good target to point to. So doing this is my compromise. :-)
thank you for an explanation. I have never used this feature before.
@T.J.Crowder, thank you for your time to answer this. I'm aware, that val is string and that I have to convert it. Maybe I wasn't clear enough in the question, that I only want to set the p1, p2, etc. properties based on the passed field argument. I also already tried frcCapacity[rowIndex][field] = +val;, but this has the error, that string cannot applied as index. Basically, the usual error. But in the meantime, I have a working solution, see the edited question.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.