Element has the type any implicitly when trying to access a property of a module namespace (object)

Asked

Viewed 820 times

5

Works:

import * as Icons from 'react-icons/fa';
...
const teste = 'FaAddressBook';
const Icon = Icons[teste];

Doesn’t work:

import * as Icons from 'react-icons/fa';
...
const teste:string = 'FaAddressBook';
const Icon = Icons[teste];

Error:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'typeof import("/node_modules/react-icons/fa/index")'.
  No index signature with a parameter of type 'string' was found on type 'typeof import("/node_modules/react-icons/fa/index")'.ts(7053)

inserir a descrição da imagem aqui

In props of the component I will receive the icon name as string and need to display it, but only works without Typescript.

1 answer

3

You are getting this error because, in Typescript, modules are also statically typed. So when you import all icons using the following notation:

import * as Icons from 'react-icons/fa';

Typescript will "define" the type of Icons (also called module namespace) as something like that:

type Icons = {
  FaAccessibleIcon: IconType;
  FaAccusoft: IconType;
  FaAcquisitionsIncorporated: IconType;
  // ...
};

Thus, Typescript will issue an error if you try to access from this module a property that can not exist (provided the option noImplicitAny is defined as true).

To better understand the error, just think that a string can assume any value of a string, such as FaAccessibleIcon (a string that exists in the module) or FooBarBaz (a string that can not exist in the module).

From the moment the compiler isn’t sure that what you’re trying to access is really there, the type any will be assigned, which throws the following error in the case of noImplicitAny be qualified:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type [...]
  No index signature with a parameter of type 'string' was found on type [...] ts(7053)

There are several ways to resolve this error. The simplest is to use a type assertion (assertion):

import * as Icons from 'react-icons/fa';
import { IconType } from 'react-icons/lib/esm';

const iconName: string = 'FaAddressBook';

// `Icon` é do tipo `IconType | undefined`. =)
const Icon = (Icons as Record<string, IconType | undefined>)[iconName];

I put in the Github for future reference.

With the code above, we confirm (of the verb "assert" in English) to the compiler who the namespace Icons is the type we create using the built-in type Record<K, V>. The guy we raised with the Record is equivalent to the following:

type LooseIconsType1 = {
  [key: string]: IconType | undefined;
};

// É o mesmo que:

type LooseIconsType2 = Record<string, IconType | undefined>;

In this case, we are using the most "secure" assertion, since if you have the option strictNullChecks activated, Icon may also be undefined (since the Union type IconType | undefined has been used), which will require you to check your existence before using it.

If you’re not worried about type security (which is a mistake, because one of the main benefits of Typescript is the additional security it brings), you can simply disable the option noImplicitAny or simply make an assertion to the guy any:

import * as Icons from 'react-icons/fa';

const iconName: string = 'FaAddressBook';

// `Icon` é do tipo `any`. =(
const Icon = (Icons as any)[iconName];

But I really don’t recommend this last option. If you can do something a little safer, there’s no reason not to do it. _(ツ)_/


Another option would be to use type-Guards for that. But as you can verify here and here, that still does not seem to me to be a very viable option.

To learn more about Typescript options, read this document.

  • There is a way to do this with a JSX.Element, in this case ai, you used an unprinted const, how to make it work on a React element?

Browser other questions tagged

You are not signed in. Login or sign up in order to post.