TypeScript – 类型 ‘String’ 无法用于索引 – React 动态组件名称映射

huangapple go评论74阅读模式
英文:

Typescript - Type 'String' Can't Be Used To Index - React Dynamic Component Name Map

问题

以下是已翻译的内容:

我已经让以下部分运作,并且正在尝试将其转换为TypeScript,但虽然我在某种程度上理解错误,但我不确定如何实际修复它。

我当前允许一个组件拥有一个名为“icon”的属性,它是一个字符串,然后这个字符串会被转换成一个要加载的组件名称。

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;
};

引发错误的行:

const IconElement = iconMap[icon];

这是我得到的两个错误:

元素具有隐式 'any' 类型,因为类型为 'string' 的表达式不能用于索引类型为 '{ BellIcon: (props: any) => Element; }' 的类型。
在类型 '{ BellIcon: (props: any) => Element; }' 上找不到带有类型 'string' 的参数的索引签名。ts(7053)

页面下方的代码是为了获取动态加载的组件的上下文:

<IconElement />

我尝试设置各种类型,但找不到解决问题的方法,更多地是在猜测,我实际上想理解这个问题。

英文:

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 &quot;./BellIcon&quot;;

export const iconMap = {
  BellIcon
};

iconButton.tsx

import { iconMap } from &quot;./icons&quot;;

type IconButtonProps = {
  icon: string;
  label?: string;
  direction?: string;
  onClick: () =&gt; void;
};

The offending line:

const IconElement = iconMap[icon];

This is what I get as the two errors:

Element implicitly has an &#39;any&#39; type because expression of type &#39;string&#39; can&#39;t be used to index type &#39;{ BellIcon: (props: any) =&gt; Element; }&#39;.
No index signature with a parameter of type &#39;string&#39; was found on type &#39;{ BellIcon: (props: any) =&gt; Element; }&#39;.ts(7053)

Further down the page then for context I am calling the following to get the dynamic component loaded:

&lt;IconElement /&gt;

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.

答案1

得分: 3

需要将icon属性的类型更改为仅接受iconMap对象的键。

import { iconMap } from "./icons";

type IconButtonProps = {
  icon: keyof typeof iconMap;
  label?: string;
  direction?: string;
  onClick: () => void;
};
英文:

You need to change the type of the icon prop to only accept keys of the iconMap object.

import { iconMap } from &quot;./icons&quot;;

type IconButtonProps = {
  icon: keyof typeof iconMap;
  label?: string;
  direction?: string;
  onClick: () =&gt; void;
};

答案2

得分: 2

@emeraldsanto的回答是正确的,但为了添加一些更多细节:

当你定义一个对象如下:

const MyMap = {
  foo: 'bar'
};

TypeScript 在处理键时采取严格的方法,并推断类型为:

{
  foo: string;
}

这意味着只有键 "foo" 被接受为此映射中的有效属性。你可以通过明确设置类型来覆盖这一点:

const MyMap: {[key: string]: string} = {
  foo: 'bar'
};

或者

const MyMap: Record<string, string> = {
  foo: 'bar'
};

(这些之间存在一些微妙的差异,但在这种情况下将起同样的作用。)现在 TypeScript 知道任何字符串都是 MyMap 的有效键。

要更严格一些,你可以使用 as const 表示你的对象应该被视为不可变的:

const MyMap = {
  foo: 'bar'
} as const;

现在 TypeScript 以最严格的方式推断类型:

{
  readonly foo: 'bar';
}

这可能是有益的,因为现在 MyMap.foo 可以在只接受 'bar' 而不是任何字符串的上下文中使用,并且如果你尝试将其与它知道是不正确的内容进行比较,例如 if (MyMap.foo === 'spam'),TypeScript 将会给出错误。

英文:

@emeraldsanto's answer is correct, but to add some more detail:

When you define an object like

const MyMap = {
  foo: &#39;bar&#39;
};

Typescript takes a strict approach w/r/t keys and infers the type as

{
  foo: string;
}

This means that only the key &quot;foo&quot; is accepted as a valid property in this map. You can override this by setting the type explicitly:

const MyMap: {[key: string]: string} = {
  foo: &#39;bar&#39;
};

or

const MyMap: Record&lt;string, string&gt; = {
  foo: &#39;bar&#39;
};

(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:

const MyMap = {
  foo: &#39;bar&#39;
} as const;

Now TS infers the type in the strictest way:

{
  readonly foo: &#39;bar&#39;
};

This can be beneficial, as now MyMap.foo can be used in contexts that only accept &quot;bar&quot;, not any string, and TS will give you an error if you try to compare to something it knows is incorrect, like if (MyMap.foo === &quot;spam&quot;).

huangapple
  • 本文由 发表于 2023年7月7日 07:33:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/76633093.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定