Introduction: when TypeScript says "no" to a simple string
\n\nYou're writing in TypeScript, everything is going well, but suddenly the compiler throws a frightening message:
\n\nElement implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ ... }'.\n\nDoes this situation sound familiar? If you've ever worked with objects and dynamic keys in TypeScript, you've almost certainly encountered this error. It is among the top 5 most common questions on Stack Overflow for TypeScript, and for good reason.
\n\nIn this article, we will break down why this error occurs, how to fix it correctly, and — most importantly — how to design code so that it never appears at all. This article will be useful for beginners just learning TypeScript, as well as for experienced developers who want to refresh their knowledge.
\n\nWhat does this error mean? A simple explanation
\n\nTypeScript is a strictly typed language. It ensures you don't access object properties with an incorrect key type. Let's imagine a simple situation:
\n\nconst user = {\n name: 'Anna',\n age: 25,\n city: 'Moscow'\n};\n\nconst key: string = 'name';\nconsole.log(user[key]); // ❌ Error!\n\nAt first glance, everything seems logical: the user object has a name property, and we are passing the string 'name'. But TypeScript sees a problem: the type of the variable key is string, not a specific literal 'name' | 'age' | 'city'.
Imagine you go to a library and say, "Give me a book." The librarian will ask, "Which one?" — because there are thousands of books. TypeScript works the same way: it cannot guarantee that a property exists in the object for any given string.
\n\nThe error literally translates to: "Element implicitly has an 'any' type because expression of type 'string' can't be used to index". In simple terms: you are trying to access an object with a key whose type is too general.
\n\nTop 5 situations when the error occurs
\n\n1. Dynamic access to object properties
\n\nThe most common case is when you get a key from an external source (API, user input, array of keys):
\n\nconst config = {\n theme: 'dark',\n language: 'ru',\n notifications: true\n};\n\nfunction getConfigValue(key: string) {\n return config[key]; // ❌ Error!\n}\n\n2. Working with enums or constants
\n\nconst COLORS = {\n red: '#FF0000',\n green: '#00FF00',\n blue: '#0000FF'\n};\n\nfunction getColorCode(colorName: string) {\n return COLORS[colorName]; // ❌ Error!\n}\n\n3. Iterating over object keys with Object.keys()
\n\nconst scores = {\n math: 95,\n physics: 88,\n chemistry: 92\n};\n\nObject.keys(scores).forEach(key => {\n console.log(scores[key]); // ❌ Error!\n});\n\nThis is a classic "pitfall": Object.keys() returns string[], not (keyof typeof scores)[].
4. Using Map or Record with generics
\n\nconst cache: Record<string, any> = {};\nconst key: string = 'user_123';\ncache[key] = { name: 'Ivan' }; // ✅ Works\n\n// But if you type it more precisely:\ninterface UserCache {\n [id: string]: { name: string };\n}\nconst users: UserCache = {};\nconst userId: string = 'user_456';\nconsole.log(users[userId]); // ❌ Error!\n\n5. Functions that accept keys as parameters
\n\ninterface Product {\n id: number;\n title: string;\n price: number;\n}\n\nfunction updateProductField(product: Product, field: string, value: any) {\n product[field] = value; // ❌ Error!\n}\n\n5 ways to fix the error (from simple to correct)
\n\nMethod 1: Type assertion (as any) — fast but dangerous
\n\nThe simplest and "dirtiest" way