Home > Back-end >  How to dynamically set a defined property using a string key TypeScript?
How to dynamically set a defined property using a string key TypeScript?

Time:05-29

I do this normally in JavaScript:

function setArbitraryProperty(object, field, value) {
  object[field] = value;
}

Now how do I do this in TypeScript?

function setArbitraryPropertyTS(object: MyXType, field: string, value: unknown): void {
  object[field] = value;
}

I get:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'MyXType'.
  No index signature with a parameter of type 'string' was found on type 'MyXType'.

If I try using keyof:

function setArbitraryPropertyTS(object: MyXType, field: keyof MyXType, value: unknown): void {
  object[field] = value;
}

I get:

Type 'unknown' is not assignable to type 'never'.

How do I get this to work? I would simply like to create a generic function which sets an arbitrary property on a typed object, where that "arbitrary property" is a property that is explicitly defined on the object. So I basically want to use it like this:

type MyXType = {
  a: string;
  b: number;
  c: boolean;
}

const obj1 = { a: 'foo', b: 123, c: true };

setArbitraryPropertyTS(obj1, 'a', 'bar');

In my case, my setArbitraryPropertyTS is actually a quite complex function which does a whole bunch of stuff, finally updating the property, and I want to vary the logic in a bunch of different ways depending on the property I am setting. So I might have:

setArbitraryPropertyTS(obj: MyXType, field: keyof MyXType, val: unknown): void {
  // complex logic...
  obj[field] = val;
}

setA(obj: MyXType): void {
  // complex logic...
  const val = 'foo' // retrieved from logic...
  setArbitraryPropertyTS(obj, 'a', val);
}

setB(obj: MyXType): void {
  // complex logic...
  const val = 123 // retrieved from logic...
  setArbitraryPropertyTS(obj, 'b', val);
}

setC(obj: MyXType): void {
  // complex logic...
  const val = true // retrieved from logic...
  setArbitraryPropertyTS(obj, 'c', val);
}

Any way this is possible?

CodePudding user response:

You'll need generics. Declare the object as a generic type, the field as another that's a key of the object type, and the value as something assignable to that key of the object.

function setArbitraryPropertyTS<
    T extends object,
    F extends keyof T,
    V extends T[F]
>(
    object: T,
    field: F,
    value: V
) {
    object[field] = value;
}

const obj1 = { a: 'foo', b: 123, c: true };

setArbitraryPropertyTS(obj1, 'a', 'bar');
  • Related