TL;DR
If you're seeing Element implicitly has an 'any' type because expression of type 'string' can't be used to index type, the cause is a variable typed as string being used to access a property on an object whose keys are a narrower union. Fix it by narrowing the variable to keyof typeof the object, adding an index signature, or using a Record type.
If that doesn't work, scroll to verify the fix — there are two common variants this guide also covers.
- Symptom: TS7053 when indexing an object with a string variable
- Root cause: The string variable is wider than the object's key union
-
Fix: Narrow the key with
keyof typeof, add an index signature, or useRecord -
Verification:
tsc --noEmitpasses and the filtered array contains the expected items
What you'll see
The error appears when you try to access an object property using a variable that TypeScript has inferred as string. The exact message from the original Stack Overflow question is:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ train_1: boolean; train_2: boolean; train_3: boolean; train_4: boolean; }'.
No index signature with a parameter of type 'string' was found on type '{ train_1: boolean; train_2: boolean; train_3: boolean; train_4: boolean; }'
It fires when a callback parameter — like name in a .filter() — is inferred as string, and you use it to index a lookup object whose keys are a specific union of string literals. The same error appears with Object.keys() loops, dynamic property accessors, and any place a plain string meets a typed object.
Root cause — Why TypeScript throws TS7053 on dynamic property access
TypeScript's job is to guarantee that every property access returns a value of a known type. When you write plotOptions["train_1"], the compiler sees the literal "train_1" and knows it is one of the four keys on the object. It can return boolean with confidence.
When you write plotOptions[name] and name is typed as string, the compiler cannot make that guarantee. A string can be "train_1", but it can also be "anything_else", "__proto__", or an empty string. None of those are guaranteed to exist on the object. Since TypeScript cannot prove the property exists, it refuses to infer a return type — and reports the element as implicitly any.
The relevant code path from the question is:
interface trainInfo {
name: string;
x: Array<number>;
y: Array<number>;
type: string;
mode: string;
}
const plotData: Array<trainInfo> = [
{ name: "train_1", x: [], y: [], type: "scatter", mode: "lines" },
{ name: "train_2", x: [], y: [], type: "scatter", mode: "lines" },
{ name: "train_3", x: [], y: [], type: "scatter", mode: "lines" },
{ name: "train_4", x: [], y: [], type: "scatter", mode: "lines" }
];
const plotOptions = {
train_1: true,
train_2: true,
train_3: true,
train_4: true
};
// TS7053: name is string, plotOptions keys are a narrower union
const filtered = plotData.filter(({ name }) => plotOptions[name]);
The name property on trainInfo is declared as string. When you destructure it in the filter callback, TypeScript keeps that string type. The plotOptions object has an inferred type with exactly four keys. A string is wider than that union, so the index operation fails.
The fix — Narrow the key type
The core fix is to make the key variable's type match the object's key union. There are seven ways to do this, each appropriate for a different situation.
Fix 1 — Use keyof typeof to constrain the variable
When the object is a constant you control, extract its key union and use it as the type for the indexing variable. This is the accepted answer's approach and the most type-safe option.
interface trainInfo {
name: keyof typeof plotOptions;
x: Array<number>;
y: Array<number>;
type: string;
mode: string;
}
const plotOptions = {
train_1: true,
train_2: true,
train_3: true,
train_4: true
};
const plotData: Array<trainInfo> = [
{ name: "train_1", x: [], y: [], type: "scatter", mode: "lines" },
{ name: "train_2", x: [], y: [], type: "scatter", mode: "lines" },
{ name: "train_3", x: [], y: [], type: "scatter", mode: "lines" },
{ name: "train_4", x: [], y: [], type: "scatter", mode: "lines" }
];
const filtered = plotData.filter(({ name }) => plotOptions[name]);
Now name is typed as "train_1" | "train_2" | "train_3" | "train_4", which is exactly the key union of plotOptions. The index operation is valid, and the compiler returns boolean for every access.
Fix 2 — Add an index signature to the target type
If the keys genuinely can be any string — for example, when they come from an API response — add an index signature to the object's type. This tells TypeScript that any string key returns the value type.
type tplotOptions = {
[key: string]: boolean;
};
const plotOptions: tplotOptions = {
train_1: true,
train_2: true,
train_3: true,
train_4: true
};
const name: string = "train_1";
const value: boolean = plotOptions[name]; // OK
This works because the index signature declares that every string key maps to a boolean. The tradeoff is that you lose the exhaustiveness check — TypeScript will happily accept plotOptions["train_999"] and return boolean, even though the key does not exist at runtime.
Fix 3 — Apply a type assertion (as keyof Type)
When you know the string is a valid key but cannot express that in the type system, use a type assertion. This is the quickest fix and the one from the second-highest-voted answer.
const someObj = {
username: "mahdi",
email: "mahdi@example.com"
};
const field = "username";
// TS7053 without the assertion
const temp = someObj[field as keyof typeof someObj];
The assertion tells TypeScript "trust me, this string is one of the keys." It silences the error but does not add any runtime safety. If field is "password" at runtime, the result is undefined and TypeScript will not have warned you.
Fix 4 — Use the in operator with a mapped type
When you need a type that has specific keys but also allows iteration, a mapped type with the in operator gives you both. This is the solution for the related error "An index signature parameter type cannot be a union type."
type TrainName = "train_1" | "train_2" | "train_3" | "train_4";
type PlotOptions = {
[K in TrainName]: boolean;
};
const plotOptions: PlotOptions = {
train_1: true,
train_2: true,
train_3: true,
train_4: true
};
const name: TrainName = "train_1";
const value: boolean = plotOptions[name]; // OK
The mapped type creates a type with exactly the four keys, each typed as boolean. Unlike an index signature, it preserves exhaustiveness — you cannot access plotOptions["train_999"] without a type error. I cover the union-parameter restriction in detail in Fix An index signature parameter type cannot be a union.
Fix 5 — Use the Record utility type
Record<K, V> is a built-in shortcut for a mapped type. It is equivalent to { [P in K]: V } and reads more clearly when the key union is already defined.
type TrainName = "train_1" | "train_2" | "train_3" | "train_4";
const plotOptions: Record<TrainName, boolean> = {
train_1: true,
train_2: true,
train_3: true,
train_4: true
};
const name: TrainName = "train_1";
const value: boolean = plotOptions[name]; // OK
Record is the idiomatic choice when you have a union of keys and a single value type. It is identical to the mapped type in Fix 4 but shorter.
Fix 6 — Handle Object.keys() and array filtering safely
Object.keys() returns string[] by design, even when called on a typed object. This means iterating with Object.keys always triggers TS7053 unless you cast each key.
class MyClass {
username = "mahdi";
email = "mahdi@example.com";
logValues() {
Object.keys(this).forEach((key) => {
console.log(this[key as keyof MyClass]);
});
}
}
The cast key as keyof MyClass is safe here because Object.keys only returns keys that actually exist on the object at runtime. The assertion is a formality to satisfy the type system, not a risk.
For array filtering, the pattern from the original question is the canonical example. The fix is to type the name property as keyof typeof plotOptions rather than string, as shown in Fix 1.
Fix 7 — Use an enum for key types
If your keys are a known set of string constants, define an enum and use it as the key type. This gives you both type safety and runtime values.
enum TrainName {
train_1 = "train_1",
train_2 = "train_2",
train_3 = "train_3",
train_4 = "train_4"
}
const plotOptions: Record<TrainName, boolean> = {
[TrainName.train_1]: true,
[TrainName.train_2]: true,
[TrainName.train_3]: true,
[TrainName.train_4]: true
};
const name: TrainName = TrainName.train_1;
const value: boolean = plotOptions[name]; // OK
Enums are useful when the keys are used in multiple places and you want a single source of truth. They also work with keyof typeof if you need the union of values.
Verify the fix
Run the TypeScript compiler on your project:
npx tsc --noEmit
You should see no output — meaning no type errors — instead of the TS7053 message. If you want to confirm the filtered array contains the expected items, add a quick runtime check:
const filtered = plotData.filter(({ name }) => plotOptions[name]);
console.log(filtered.length); // 4 when all plotOptions are true
If you're still seeing the error, two common variants exist:
Variant A — You added an index signature but the error persists
This happens when you add [key: string]: boolean to the target object's type but the indexing variable is still typed as string in a context where the index signature is not visible. Check that the variable's type is actually narrowed — an index signature on the object does not change the type of the variable doing the indexing. You need either the index signature on the object and a string variable, or a narrowed variable and a plain object type. Mixing a narrowed variable with an index signature is redundant but harmless.
Variant B — The string comes from an API response
When the key comes from fetch() or a JSON payload, you cannot know at compile time whether it is a valid key. Use an index signature or Record<string, T> for the target object, and add a runtime guard before indexing:
const response = await fetch("/api/plot-options");
const data: Record<string, boolean> = await response.json();
const name: string = "train_1";
if (name in data) {
const value = data[name]; // OK, guarded
}
Why this happens (and how to avoid it next time)
The invariant is simple: TypeScript will only let you index an object with a key whose type is assignable to the object's key union. A string is never assignable to a union of string literals, so the compiler rejects the access. The fix is always to narrow the key, widen the object, or assert.
To prevent this from recurring, enable noImplicitAny in your tsconfig.json — it is on by default with strict: true and is what surfaces this error in the first place. Do not disable it with suppressImplicitAnyIndexErrors; that flag hides the problem instead of fixing it. If you are working in a Next.js or Supabase project, the same patterns apply — I cover the broader type-safety setup in Type Safety Guide for Next.js + Supabase in TypeScript.
For related TypeScript errors, see JSX.Element vs ReactNode vs ReactElement: TS2322 Fix and Fix TS2305: Module Has No Exported Member in TypeScript.
FAQ
Why does TypeScript give "Element implicitly has an any type" when using a string to access an object property?
TypeScript throws TS7053 because a variable typed as string can hold any value, not just the known keys of the object. The compiler cannot guarantee the property exists, so it refuses to infer a return type. Narrow the variable to keyof typeof the object or add an index signature.
Can I just use "as keyof" to suppress the TypeScript indexing error?
Yes, a type assertion like someObj[field as keyof typeof someObj] silences the error, but it bypasses compile-time safety. Use it only when you are certain the string is a valid key, such as when iterating with Object.keys on a known object.
What is the difference between [key: string] and Record<string, type>?
They are functionally identical for string keys. Record<string, boolean> is a mapped type that produces { [key: string]: boolean }. Record is shorter and works with any key union, while an index signature is limited to string, number, or symbol.
How do I iterate over object keys with Object.keys without getting the 'any' type error?
Cast each key inside the loop: Object.keys(obj).forEach((key) => console.log(obj[key as keyof typeof obj])). The cast is safe because Object.keys only returns keys that exist at runtime.
Related
- Fix An index signature parameter type cannot be a union
- Type Safety Guide for Next.js + Supabase in TypeScript
- JSX.Element vs ReactNode vs ReactElement: TS2322 Fix
- Fix TS2305: Module Has No Exported Member in TypeScript
- Force tsc to Ignore node_modules: Fix TS Errors
Originally published at https://www.iloveblogs.blog
Top comments (0)