TypeScript报错:string索引Strapi组件映射对象致隐式any求解
报错原文
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ 'sections.hero': ({ sectionData, key }: props) => Element; }'. No index signature with a parameter of type 'string' was found on type '{ 'sections.hero': ({ sectionData, key }: props) => Element; }'.
问题根因
- 代码中
sectionData.__component被定义为宽泛的string类型,但sectionComponents对象的键是固定字面量'sections.hero',TypeScript 严格模式下不允许用任意字符串索引键类型明确的对象,避免访问不存在的属性导致运行时错误。 - 现有代码中
Sections组件的sections属性被定义为空元组类型[],后续访问section.__component、section.id时也会触发类型错误,需要同步修复。
修复方案
方案1:严格类型约束(推荐,类型安全度最高)
通过as const锁定组件映射表的键类型,提取合法键的联合类型约束__component的取值范围,同时补全所有section数据的类型定义,从根源避免类型不匹配:
import type { ComponentType } from 'react'; // 定义Hero组件接收的属性类型,后续新增其他区块组件可按相同方式扩展 type HeroSection = { __component: 'sections.hero'; id: string | number; // 在此处补全Hero区块需要的其他业务字段 [key: string]: unknown; }; // Hero组件的实际定义从对应文件引入即可,此处为示例声明 declare const Hero: ComponentType<{ data: HeroSection }>; // 组件映射表加as const,锁定键的字面量类型 const sectionComponents = { 'sections.hero': Hero, } as const; // 自动提取映射表所有合法组件名类型 type ValidSectionKey = keyof typeof sectionComponents; // 聚合所有区块数据的联合类型,后续新增区块直接加进联合类型即可 type SectionData = HeroSection; type SectionProps = { sectionData: SectionData; }; const Section = ({ sectionData }: SectionProps) => { // 此处TS可自动识别__component为合法键,无报错 const SectionComponent = sectionComponents[sectionData.__component]; if (!SectionComponent) return null; return <SectionComponent data={sectionData} />; }; // 补全sections的数组类型定义 const Sections = (props: { sections: SectionData[] }) => { return ( <> {props.sections.map((section) => ( <Section sectionData={section} key={`${section.__component}${section.id}`} /> ))} </> ); }; export default Sections;
优势:后续新增区块组件时,只要在sectionComponents中添加对应键值、将新的区块数据类型加入SectionData联合类型,TS会自动完成全链路校验,组件名拼写错误、漏传组件props等问题都会在编译阶段提前暴露。
方案2:类型断言快速修复(适合临时迭代场景)
如果暂时不需要补全全量类型,可通过类型断言告诉TS当前索引值是映射表的合法键,同时修复sections的类型定义即可:
// 仅修改报错行和Sections的props定义即可 const Section = ({ sectionData }: sectionDataProps) => { // 加as keyof typeof sectionComponents做类型断言 const SectionComponent = sectionComponents[sectionData.__component as keyof typeof sectionComponents]; if (!SectionComponent) return null; return <SectionComponent data={sectionData} />; }; // 修改sections的类型定义,避免访问属性报错 const Sections = (props: { sections: Array<{__component: string; id: string | number; [key: string]: unknown}> }) => { // 原有逻辑不变 return ( <> {props.sections.map((section) => ( <Section sectionData={section} key={`${section.__component}${section.id}`} /> ))} </> ); };
注意:该方案会跳过对__component传值的合法性校验,若传入映射表中不存在的组件名,TS不会提前报错,仅能靠运行时的非空判断返回null兜底。
方案3:添加索引签名(不推荐,类型安全度最低)
直接给组件映射表添加字符串索引签名,允许任意字符串作为键访问:
import type { ComponentType } from 'react'; type sectionDataProps = { sectionData: { __component: string; [key: string]: unknown; }; }; const sectionComponents: { [key: string]: ComponentType<{data: sectionDataProps['sectionData']}> | undefined; 'sections.hero': typeof Hero; } = { 'sections.hero': Hero, };
注意:该写法会丢失对组件名的严格校验,容易因为组件名拼写错误导致运行时渲染异常,非特殊场景不建议使用。
内容的提问来源于stack exchange,提问作者Sygix

