Here is an example where such errors can occur:
const obj = {
'/about': 'bg-orange',
'/contact': 'bg-purple',
'/services': 'bg-yellow'
}
function getClassName(key: string) {
return /**/obj[key]/**/
}
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ '/about': string; '/contact': string; '/services': string; }'...No index signature with a parameter of type 'string' was found on type '{ '/about': string; '/contact': string; '/services': string; }' What is happening here? Let me explain.
TypeScript inferred the type obj
to be this:
const obj: {
'/about': string;
'/contact': string;
'/services': string;
}
Implicitly, it means the keys you can use with this object has this union literal type:
'/about' | '/contact' | '/services'
So, you can access the properties in this object only those strings:
const obj: {
'/about': string;
'/contact': string;
'/services': string;
}
obj['/about'] // fine
obj['/services'] // fine
const variable = "/contact"
obj[variable] // fine
But any other string, does not fall in the union:
const obj: {
'/about': string;
'/contact': string;
'/services': string;
}
obj['/about'] // fine
obj['/services'] // fine
const variable = "/contacts"
obj/**/[variable]/**/
Property '/contacts' does not exist on type '{ '/about': string; '/contact': string; '/services': string; }' /**/obj["/sss"]/**/
Element implicitly has an 'any' type because expression of type '"/sss"' can't be used to index type '{ '/about': string; '/contact': string; '/services': string; }'.,,Property '/sss' does not exist on type '{ '/about': string; '/contact': string; '/services': string; }' Solution
The solution here is to provide a string that falls into the union. Back to our function above:
const obj = {
'/about': 'bg-orange',
'/contact': 'bg-purple',
'/services': 'bg-yellow'
}
function getClassName(key: string) {
return obj[key]
}
Instead of defining the key
parameter as string
, we need to pass a type that falls in the keys. You can do this manually like this:
const obj = {
'/about': 'bg-orange',
'/contact': 'bg-purple',
'/services': 'bg-yellow'
}
type Path = '/about' | '/contact' | '/services'
function getClassName(key: Path) {
return obj[key]
}
Here, key
isn’t just “any string”, but a string that falls in the object’s keys.
But here, you are repeating yourself. If you add a new property, you also need to add that to Path
:
const obj = {
'/about': 'bg-orange',
'/contact': 'bg-purple',
'/services': 'bg-yellow',
'/products': 'bg-green'
}
type Path = '/about' | '/contact' | '/services' | 'products'
Instead, you can do this:
const obj = {
'/about': 'bg-orange',
'/contact': 'bg-purple',
'/services': 'bg-yellow'
}
type Path = keyof typeof obj
function getClassName(key: Path) {
return obj[key]
}
const obj = {
'/about': 'bg-orange',
'/contact': 'bg-purple',
'/services': 'bg-yellow'
}
type /**/Path/**/ = keyof typeof obj
"/about" | "/contact" | "/services" Now, if you add more properties to obj
, the keys will be available as union in Path
. You can learn more about this my keyof typeof magic tip.