Back to blog

No Implicit Any Type String Index Error

4 min read

Cover image for No Implicit Any Type String Index Error
Image crafted by robots

TL;DR

TS7053 shows up when you index an object with a string key that TypeScript cannot prove is valid.

Fix it by doing one of these:

The problem

Here is the error:

Error ts(7053) ― Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ name: string; type: string; }'. No index signature with a parameter of type 'string' was found on type '{ name: string; type: string; }'.

You usually see it with noImplicitAny enabled, often through strict in tsconfig.json.

The core issue is simple.

This compiles in JavaScript:

item[key];

But TypeScript has to answer a question first.

Is key actually one of the keys on item?

If key: string, the answer is “could be anything”.

So TypeScript blocks it.

const
const item: {
name: string;
type: string;
}
item
= {
name: string
name
: "Apple",
type: string
type
: "fruit",
};
function
function logValue(key: string): void
logValue
(
key: string
key
: string) {
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
(
const item: {
name: string;
type: string;
}
item
[
key: string
key
]);
Error ts(7053) ― Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ name: string; type: string; }'. No index signature with a parameter of type 'string' was found on type '{ name: string; type: string; }'.
}

item has two keys: name and type.

key could be "lol", "price", or "__proto__".

TypeScript does not let you pretend those are safe.

Solutions

Pick the fix based on where the key comes from.

1. You know the key set, so restrict it with keyof

This is the cleanest fix when callers should only use real keys.

const
const item: {
name: string;
type: string;
}
item
= {
name: string
name
: "Apple",
type: string
type
: "fruit",
};
function
function logValue(key: keyof typeof item): void
logValue
(
key: "name" | "type"
key
: keyof typeof
const item: {
name: string;
type: string;
}
item
) {
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
(
const item: {
name: string;
type: string;
}
item
[
key: "name" | "type"
key
]);
}

If someone tries logValue("price"), TypeScript stops them.

This is what you want when you control the input.

2. You do not control the key, so check at runtime

If the key comes from user input, query params, API data, or config, then it is string.

At that point you need a runtime check.

const
const item: {
name: string;
type: string;
}
item
= {
name: string
name
: "Apple",
type: string
type
: "fruit",
};
function
function logValue(key: string): void
logValue
(
key: string
key
: string) {
if (
key: string
key
in
const item: {
name: string;
type: string;
}
item
) {
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
(
const item: {
name: string;
type: string;
}
item
[
key: string
key
as keyof typeof
const item: {
name: string;
type: string;
}
item
]);
}
}

This keeps runtime behavior honest.

The remaining assertion exists because key in item narrows at runtime, but TypeScript still sees key as string without a stronger predicate.

If you want the narrowing to be fully typed, use a type guard.

3. Make the runtime check a type guard

This gives you both runtime safety and clean types inside the branch.

const
const item: {
name: string;
type: string;
}
item
= {
name: string
name
: "Apple",
type: string
type
: "fruit",
};
type
type KeyOfItem = "name" | "type"
KeyOfItem
= keyof typeof
const item: {
name: string;
type: string;
}
item
;
function
function isKeyOfItem(key: string): key is KeyOfItem
isKeyOfItem
(
key: string
key
: string):
key: string
key
is
type KeyOfItem = "name" | "type"
KeyOfItem
{
return
key: string
key
in
const item: {
name: string;
type: string;
}
item
;
}
function
function logValue(key: string): void
logValue
(
key: string
key
: string) {
if (
function isKeyOfItem(key: string): key is KeyOfItem
isKeyOfItem
(
key: string
key
)) {
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
(
const item: {
name: string;
type: string;
}
item
[key]);
key: "name" | "type"
}
}

This is my default when keys are dynamic.

It reads well and it scales.

4. You want a quick escape hatch, so assert

This silences the error, but it does not validate anything.

const
const item: {
name: string;
type: string;
}
item
= {
name: string
name
: "Apple",
type: string
type
: "fruit",
};
function
function logValue(key: string): void
logValue
(
key: string
key
: string) {
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
(
const item: {
name: string;
type: string;
}
item
[
key: string
key
as keyof typeof
const item: {
name: string;
type: string;
}
item
]);
}

If key is not real, you get undefined.

Use this only when you have a guarantee TypeScript cannot see.

5. The object is intentionally open-ended, so add an index signature

If the object genuinely allows arbitrary string keys, model it that way.

const
const item: {
[key: string]: string;
}
item
: { [
key: string
key
: string]: string } = {
name: string
name
: "Apple",
type: string
type
: "fruit",
};
function
function logValue(key: string): void
logValue
(
key: string
key
: string) {
const value =
const item: {
[key: string]: string;
}
item
[
key: string
key
];
const value: string
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
(
const value: string
value
);
}

This trades precision for flexibility.

Be careful. You just told TypeScript that every property is a string, even ones that do not exist.

If you enable noUncheckedIndexedAccess, TypeScript will force you to handle missing keys.

tsconfig.json
{
"compilerOptions": {
"noUncheckedIndexedAccess": true
}
}

Then this becomes string | undefined instead of pretending it is always there.

const
const item: {
[key: string]: string;
}
item
: { [
key: string
key
: string]: string } = {
name: string
name
: "Apple",
type: string
type
: "fruit",
};
function
function logValue(key: string): void
logValue
(
key: string
key
: string) {
const value =
const item: {
[key: string]: string;
}
item
[
key: string
key
];
const value: string | undefined
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
(
const value: string | undefined
value
);
}

6. Do not do this unless you mean it

A permissive index signature defeats the point.

interface
interface Item
Item
{
[
key: string
key
: string]: any;
Item.name: string
name
: string;
Item.quantity: number
quantity
: number;
}
const
const item: Item
item
:
interface Item
Item
= {
Item.name: string
name
: "Apple",
Item.quantity: number
quantity
: 6,
};
function
function logValue(key: string): void
logValue
(
key: string
key
: string) {
const value =
const item: Item
item
[
key: string
key
];
const value: any
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
(
const value: any
value
);
}

This says “any string key is fine”, and then TypeScript can no longer protect you.

If you reach for this, pause.

You are probably hiding a modeling problem.

Wrapping up

TS7053 is TypeScript forcing you to be honest about dynamic keys.

Once you pick the right shape, the error goes away and your code stays predictable.

Questions or feedback? Send me an email.

Last updated on

Back to blog