React中结合Interface使用useState处理对象的问题及解决
问题背景
定义了如下TypeScript接口用于描述稿件结构:
export interface IManuscript { heading: IManuscriptHeading; abstract: IShortSection; sections: ISection; } interface IManuscriptHeading { title: string; authors: Array<IAuthors>; } interface IAuthors { first_name: string; last_name: string; url: string; } interface IShortSection { title: string; description: Array<string>; } interface ISection { introduction: { title: string; description: Array<string>; }; hypothesis_development: IHypothesis; method: ISectionProps; results: ISectionProps; study_limitations: ISectionProps; conclusions: ISectionProps; } interface IHypothesis { short_title: string; title: string; figures: IFigure description: Array<string>; } interface IFigure { name: string; title: string; legend: string; } interface ISectionProps { title: string; description: Array<ISubsection>; } interface ISubsection { title: string; figures: Array<IFigure> tables: { title: string; data: []} description: Array<string>; }
在React组件中使用时遇到了一系列类型错误:
- 初始状态赋值错误:
const [manuscript, setManuscript] = useState<IManuscript>({})
报错:Argument of type '{}' is not assignable to parameter of type 'IManuscript'
- 改用
Partial后,组件属性传递报错:
const [manuscript, setManuscript] = useState<Partial<IManuscript>>({}); <SectionHeader title={manuscript.heading.title} />
报错:manuscript.heading is possibly undefined
- 使用可选链修复后,遍历sections时出现语法错误:
{Object.keys(manuscript?.sections?).map((key: any, index: number) => (<div></div>))}
报错:Expression expected
错误原因与修复方案
1. 初始状态赋值错误
IManuscript接口的所有属性都是必填项,空对象{}完全不符合该类型结构。使用Partial<IManuscript>是正确的选择,它会将接口的所有属性转为可选类型,允许初始值为空对象。
2. manuscript.heading is possibly undefined错误
Partial<IManuscript>让heading、sections等属性都变成了可选,因此这些属性可能为undefined。使用可选链manuscript?.heading?.title是正确的修复方式,它会在属性不存在时返回undefined,避免运行时报错。
3. Expression expected错误
manuscript?.sections的返回值可能是undefined,而Object.keys()的参数必须是对象类型,不能接收undefined。TypeScript会因此抛出语法错误,因为它无法确定传入Object.keys的是合法参数。
修复方式有两种:
方式一:使用空值合并运算符提供默认空对象
{Object.keys(manuscript?.sections ?? {}).map((key: keyof ISection, index: number) => ( <div key={index}>{key}</div> ))}
?? {}会在manuscript?.sections为undefined时,传入一个空对象给Object.keys,避免类型错误。同时建议将key的类型从any改为keyof ISection,获得更严格的类型检查。
方式二:先判断sections是否存在再遍历
{manuscript?.sections && Object.keys(manuscript.sections).map((key: keyof ISection, index: number) => ( <div key={index}>{key}</div> ))}
通过&&短路判断,只有当manuscript?.sections存在时才执行后续的Object.keys和map操作。
额外优化建议
如果不想在代码中频繁使用可选链,可以给初始状态设置一个符合Partial<IManuscript>的默认结构,确保所有层级的属性都有初始值:
const [manuscript, setManuscript] = useState<Partial<IManuscript>>({ heading: { title: '', authors: [] }, abstract: { title: '', description: [] }, sections: { introduction: { title: '', description: [] }, hypothesis_development: { short_title: '', title: '', figures: { name: '', title: '', legend: '' }, description: [] }, method: { title: '', description: [] }, results: { title: '', description: [] }, study_limitations: { title: '', description: [] }, conclusions: { title: '', description: [] } } });
这样后续使用时可以直接访问manuscript.heading.title,无需添加可选链,同时保持类型安全。
内容的提问来源于stack exchange,提问作者user1828605

