How to Iterate Over String Indexed Array In TypeScript?
Asked Answered
B

2

13

I have defined a static property as such:

private static colorsByName: { [index: string]: MyColorClass}

but when I attempt to use for... of from the answer listed here: TypeScript for-in statement

for(let value of MyClass.colorsByName) {
    ...
}

I get an error:

Type { [index: string]: MyColorClass; } is not an array type or a string type.

If I switch over to using for in, the error goes away, but value is typed as any.

for(let value of MyClass.colorsByName) {
    ...
}

What is the actual type of value in this case? Ideally I'd like to loop through all values in the colorsByName property, either in a pair approach, or just to get MyColorClass types returned.

for(let value of MyClass.colorsByName) {
    // value: MyColorClass
}

What are my options?

Berglund answered 12/2, 2016 at 19:10 Comment(0)
N
17

It's not an array—it's an object with string keys and values of type MyColorClass.

What you can do, is turn it into an array by getting an array of the object's keys then mapping the keys to the properties of the object:

const colors = Object.keys(MyClass.colorsByName).map(key => MyClass.colorsByName[key]);

Since you might do this a lot, you could create a reusable function to turn the properties into an array:

function propsToArray<T>(obj: { [index: string]: T; } | { [index: number]: T; }) {
    return Object.keys(obj).map(prop => obj[prop]);
}

Then you use it like so:

for (const color of propsToArray(MyClass.colorsByName)) {
    // use color here
}

Side note: You may just want to store this cached on a static property of MyClass.

Object.values

Alternatively, you could also use Object.values():

for (const color of Object.values(MyClass.colorsByName)) {
    // use color here
}

But you might need to add a polyfill if you use that.

Niple answered 12/2, 2016 at 19:12 Comment(4)
So would Object.keys(MyClass.colorsByName) potentially return back, "red","black","green" then calling the map does a projection of that enumeration?Berglund
so { [index: string]: MyColorClass} does not mean an array that is indexed by string, but rather an object whose properties are of type MyColorClass, and those properties are indexed by string?Berglund
the for..in (below, by @LukeMachowski ) seems to be better. Please consider to update correct answerMyopic
@Myopic generally using Object.keys is preferred over for...in (see here). In this situation I don't think it matters which one is used, but I just have a general rule of always using Object.keys so I don't run into any surprises.Niple
F
7

for..in

When looking at the Typescript documentation (Typescript: Iterators and Generators), we see that the for..in syntax will iterate over the keys of the object.

for..in returns a list of keys on the object being iterated, whereas for..of returns a list of values of the numeric properties of the object being iterated.

We can use that to our advantage to index into our object and get the strongly typed value:

// Go through each key of the indexed object:
for (const key in indexedObject)
{
   // Get the indexed item by the key:
   const indexedItem = indexedObject[key];
   // Now we have the item.

   // Use it...
}

Solution

We can use that to get an elegant solution to the question:

// Go through each named color:
for (const colorName in colorsByName)
{
   // Get the strongly typed color with this name:
   const color = colorsByName[colorName]; // : MyColorClass
   // Now we have the the strongly typed color with this name.

   // Paint the world in a techni-colour rainbow...
}
Flaxman answered 4/3, 2018 at 11:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.