Eli Himself
Eli Himself

Reputation: 904

TypeScript map over Object keys and values: Element implicitly has an 'any' type

I am trying to iterate over object keys and values but TypeScript is shouting at me that:

Element implicitly has an any type because of type string can't be used to index type { name: string; surname: string; gender: string; }

What am I doing wrong?

const DATA = {
  name: "John",
  surname: "Smith",
  gender: "Male"
}

const result = Object.keys(DATA).map((d: string) => `${d} - ${DATA[d]}`)

Here is a screenshot of the error:

screenshot of typescript error

Upvotes: 12

Views: 30236

Answers (5)

Eiliya Abedianamiri
Eiliya Abedianamiri

Reputation: 31

You should cast the result of Object.keys(DATA) to (keyof typeof DATA)[] since Object.keys by default returns string[].

const DATA = {
  name: "John",
  surname: "Smith",
  gender: "Male"
}

const keys = Object.keys(DATA) as (keyof typeof DATA)[];
const result = keys.map((d) => `${d} - ${DATA[d]}`);

Also, make sure you don't cast the iterating variable d to string. By not casting it to string, its type will be automatically inferred.

Upvotes: 0

Christopher Peisert
Christopher Peisert

Reputation: 24114

Error

Element implicitly has an any type because expression of type string can't be used to index type {name: string; surname: string; gender: string; }.

No index signature with a parameter of type string was found on type ...

Solution 1: Define object as Record type

const DataRecord: Record<string, string> = {
  name: "Johnny-come-lately",
  surname: "Smithers",
  gender: "Male"
}

for (const key of Object.keys(DataRecord)) {
  console.log(`${key}: ${DataRecord[key]}`);
}

Solution 2: Define object as an Indexable Type

const DataIndexableType: {[key: string]: string} = {
  name: "Johnny-come-lately",
  surname: "Smithers",
  gender: "Male"
}

for (const key of Object.keys(DataIndexableType)) {
  console.log(`${key}: ${DataIndexableType[key]}`);
}

Solution 3: Use Object.entries()

If for some reason it is not practical to add type information for the object, Object.entries() may be used without using type casting.

const DATA = {
  name: "John",
  surname: "Smith",
  gender: "Male"
}

// Object.entries
for (const [key, value] of Object.entries(DATA)) {
    console.log(`${key}: ${value}`);
}

// Or Object.entries.map
Object.entries(DATA).map( ([key, value]) => console.log(`${key}: ${value}`));

Solution 4: Use a typecast on the keys

const DATA = {
  name: "John",
  surname: "Smith",
  gender: "Male"
}

// Object.keys with typecast on key.
for (const key of Object.keys(DATA)) {
  console.log(`${key}: ${DATA[key as keyof typeof DATA]}`);
}

Solution 5: Use a typecast on the object indicating indexability

const DATA = {
  name: "John",
  surname: "Smith",
  gender: "Male"
}

// Object.keys with typecast on object.
for (const key of Object.keys(DATA)) {
  console.log(`${key}: ${(DATA as {[key: string]: string})[key]}`);
}

Upvotes: 20

Siraj Alam
Siraj Alam

Reputation: 10025

Just cast the string returned from the Object.keys into the key of the Data object.

const DATA = {
  name: "John",
  surname: "Smith",
  gender: "Male"
}

const result = Object.keys(DATA).map((d: string) => `${d} - ${DATA[d as keyof typeof DATA]}`)

Playground Link

Upvotes: 9

SJT
SJT

Reputation: 252

The Object's keys themselves are not explicitly typed as strings.

When you do

Object.keys(DATA)

You're getting name, surname, gender as values to pass to map, not John, Smith, Male.

To type the keys as well you need to type the object like this:

type DataType = {[key:string]: string }
const DATA:DataType = 
{
 name: "John",
 surname: "Smith",
 gender: "Male"
}

Upvotes: 0

Reyno
Reyno

Reputation: 6505

You need to tell typescript that the keys of your object are of type string.

const DATA: {[key: string]: string} = {
  name: "John",
  surname: "Smith",
  gender: "Male"
}

const result = Object.keys(DATA).map((d: string) => `${d} - ${DATA[d]}`)

Upvotes: 2

Related Questions