Home > OS >  Dynamically assigning object properties based on the value of an array of other objects
Dynamically assigning object properties based on the value of an array of other objects

Time:11-28

Okay so I know the title can be a bit confusing, so let me explain.

I have the following interfaces and an array of Items[]:

interface Items {
    step: number;
    keyName: string;
    value: string;
}

interface ReturnData {
    foo: string;
    bar: string;
    baz: string;
}

const items: Items[] = [
    { step: 1, keyName: 'foo', value: 'fooVal' },
    { step: 2, keyName: 'bar', value: 'barVal' },
    { step: 3, keyName: 'baz', value: 'bazVal' },
];

Now imagine I want function that accepts Items[] as a parameter, loops over the items, and for each item assigns a new property with a key of keyName and a value of value to a new object of type ReturnData, and then it finally returns that object as a promise.

For demonstration purposes I am using setInterval here, but the same would work with a for loop or other method.

const myFunc = (items: Items[]): Promise<ReturnData> => {
    let index = 0;
    let returnData: ReturnData = { foo: '', bar: '', baz: '' };

    return new Promise(resolve => {
        const interval = setInterval(() => {

            /*
                at index 0: items[index].keyName = 'foo' and items[index].value = 'fooVal'
                at index 1: items[index].keyName = 'bar' and items[index].value = 'barVal'
                at index 2: items[index].keyName = 'baz' and items[index].value = 'bazVal'
            */

            returnData[items[index].keyName] = items[index]?.value;

            if (items[index]?.step === items.length) {
                clearInterval(interval);
                resolve(returnData);
            }
            else {
                index  = 1;
            }

        }, 250);
    })
}

(async () => {
    const myResult = await myFunc(items);
    console.log('myResult value is:', myResult);

    /* myResult value is: { foo: 'fooVal', bar: 'barVal', baz: 'bazVal' } */
})();

In plain javascript this function would run properly and return myResult as expected. But in Typescript I am getting the following error messages when I try to dynamically assign properties to returnData at each iteration of my setInterval.

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

and

Object is possibly 'undefined'.ts(2532)

I have recently started learning Typescript, so my question is, how do I make Typescript understand what is happening here so that it doesn't throw the an error. I know a quick and dirty trick is to assign type any to my returnData object to get rid of the errors, but that wouldn't be exactly "type-safe" if that is the correct terminology. What is a better way to go about and make typescript happy?

Full code example at TS Playground

CodePudding user response:

Updating this line should solve your issue:

Issue is that typescript can not associate they keyName of string type as being a key of ReturnData.

interface Items {
    step: number;
    keyName: keyof ReturnData; // only if you expect that ReturnData keyName will always belong to ReturnData 
    value: string;
}

typescript or

 returnData[(items[index]!.keyName) as keyof ReturnData] = items[index]?.value ?? '';

Typescript playground

CodePudding user response:

Just change your interface like this, using keyof operator.

interface Items {
    step: number;
    keyName: keyof ReturnData;
    value: string;
}


keyName is now: "foo" | "bar" | "baz"

  • Related