Why can TypeScript index a typed object by string when that string is a constant or a simple string variable, but it's unable to index a typed object by a string if that string is pulled out of an array
That is, consider the following code
class Foo {
public bar: string = 'hello';
public test() {
// this works
console.log(this['bar'])
// this also works
const index = 'bar';
console.log(this[index])
// in both cases above I have successfully used
// a string as an index for my type Foo
// However, this doesn't work
const props:string[] = ['bar']
for(const [key,value] of props.entries()) {
console.log(value); // prints 'bar' to terminal/console
console.log(this[value])
}
// Nor does this
for(let i=0;i<props.length;i++) {
console.log(this[props[i]])
}
// when looping over an array of string and trying to use the
// string to index the object, I get the following error
// why.ts:20:25 - error TS7053: Element implicitly has an 'any'
// type because expression of type 'string' can't be used to
// index type 'Foo'.
}
}
const foo = new Foo;
foo.test()
class Foo {
public bar: string = 'hello';
public test() {
// this works
console.log(this['bar'])
// this also works
const index = 'bar';
console.log(this[index])
// in both cases above I have successfully used
// a string as an index for my type Foo
// However, this doesn't work
const props:string[] = ['bar']
for(const [key,value] of props.entries()) {
console.log(value); // prints 'bar' to terminal/console
console.log(this[value])
}
// Nor does this
for(let i=0;i<props.length;i++) {
console.log(this[props[i]])
}
// when looping over an array of string and trying to use the
// string to index the object, I get the following error
// why.ts:20:25 - error TS7053: Element implicitly has an 'any'
// type because expression of type 'string' can't be used to
// index type 'Foo'.
}
}
const foo = new Foo;
foo.test()
Both of these work.
console.log(this['bar'])
//...
const index = 'bar';
console.log(this[index])
TypeScript's able to index my object by a string.
However, the later examples where I'm looping over a string array
const props:string[] = ['bar']
for(const [key,value] of props.entries()) {
console.log(value); // prints 'bar' to terminal/console
console.log(this[value])
}
for(let i=0;i<props.length;i++) {
console.log(this[props[i]])
}
won't run/compile. I get the following error.
why.ts:42:17 - error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Foo'.
No index signature with a parameter of type 'string' was found on type 'Foo'.
42 console.log(foo[value])
So this error message -- expression of type 'string' can't be used to index type 'Foo' seems to run counter to my first two examples.
So what's going on here? Help a poor dynamic language programmer understand what TypeScript is trying to tell me. Bonus points for a sample that actually allows me to iterate over an array of strings and use one as an object index.
In TypeScript, the string is sequence of char values and also considered as an object. It is a type of primitive data type that is used to store text data.
Characters in a string are indexed from left to right. The index of the first character is 0 , and the index of the last character—in a string called stringName is stringName. length - 1 . If the index you supply is out of this range, JavaScript returns an empty string.
The error "Element implicitly has an 'any' type because expression of type 'string' can't be used to index type" occurs when we use a string to index an object with specific keys. To solve the error, type the string as one of the object's keys.
Object: It describes the functionality, object helps in representing the non-primitive types that is everything except number, string, boolean, big int, symbol, undefined and null. In TypeScript Object(O uppercased) is different from object(o lowercased).
The answer is simple, if typescript can prove the access is safe, the indexing is allowed.
When you write this['bar']
typescript sees the string literal and it can trivially check that this
has a property bar
When you write const index = 'bar';
you may think the type of index
is string
but it is actually not, the type of index
is the string literal type 'bar'
, so typescript will know the only possible value in index
is 'bar'
. Since index
can only hold bar, typescript can check that the access this[index]
is valid by checking this
has a property bar
When you write const props:string[]
typescript will not make any other inferences about props
it is an array of string
. This means when you access this[prop]
typescript needs to be sure this
is indexable by any string
, which since it does not have an index signature, it is not and thus the access throws an error. If you use as const
to make ts infer literal types for the array instead of string
and remove the explicit annotation, you will be able to perform the index access:
const props = ['bar'] as const
for(const [key,value] of props.entries()) {
console.log(value);
console.log(this[value])//ok
}
for(let i=0;i<props.length;i++) {
console.log(this[props[i]])
}
Playground Link
You can also use a type assertion if you are sure prop
is a key of this
:
const props = ['bar']
for(const [key,value] of props.entries()) {
console.log(this[value as keyof this])
}
Or if you want to get really fancy you can use a custom type guard or custom type assertion, but that seems like overkill here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With