I’ve got the following working and I’m trying to convert it to Typescript but whilst I somewhat understand the error, I’m not sure how to go about actually fixing it.
I am currently allowing a component to have a prop of "icon" which is a string and this string is then translating to a component name to be loaded.
icons/index.ts
import { BellIcon } from "./BellIcon";
export const iconMap = {
BellIcon
};
iconButton.tsx
import { iconMap } from "./icons";
type IconButtonProps = {
icon: string;
label?: string;
direction?: string;
onClick: () => void;
};
The offending line:
const IconElement = iconMap[icon];
This is what I get as the two errors:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ BellIcon: (props: any) => Element; }'.
No index signature with a parameter of type 'string' was found on type '{ BellIcon: (props: any) => Element; }'.ts(7053)
Further down the page then for context I am calling the following to get the dynamic component loaded:
<IconElement />
I’ve tried setting various types but can’t find anything that solves the problem, was more trying to guess and I’d actually like to understand the issue.
2
Answers
You need to change the type of the
icon
prop to only accept keys of theiconMap
object.@emeraldsanto’s answer is correct, but to add some more detail:
When you define an object like
Typescript takes a strict approach w/r/t keys and infers the type as
This means that only the key
"foo"
is accepted as a valid property in this map. You can override this by setting the type explicitly:or
(These have some subtle differences, but in this case will work the same.) Now TS knows that any string is a valid key for
MyMap
.To go stricter, you can use
as const
to indicate that your object should be treated as frozen:Now TS infers the type in the strictest way:
This can be beneficial, as now
MyMap.foo
can be used in contexts that only accept"bar"
, not any string, and TS will give you an error if you try to compare to something it knows is incorrect, likeif (MyMap.foo === "spam")
.