NextJS(TypeScript)子组件setState类型错误:无法追加状态至数组
嘿,我之前也遇到过完全一样的类型报错!咱们一步步来搞定它。
首先先看你碰到的错误提示:
Type error: Argument of type '(prev: Example[]) => (Example | undefined)[]' is not assignable to parameter of type 'SetStateAction<Example[]>'.
问题根源
这个错误的核心是:你在createExample的onSuccess回调里,把data添加到数组后,得到的数组类型变成了(Example | undefined)[],但你的setItems期望的是纯Example[]类型,所以TypeScript就报错了。
为什么data会可能是undefined?大概率是你的api.example.createExample的返回类型定义里,允许返回Example | undefined——可能是tRPC路由或者Prisma mutation的返回逻辑里,存在成功但不返回对象的情况,或者TypeScript没推断出它一定是有效的Example。
解决方案(按推荐程度排序)
1. 确保data是确定的Example类型(最优解)
先去检查你的API定义(比如tRPC的router或者Prisma的mutation函数),确保createExample成功后必然返回一个有效的Example对象。如果API本身已经保证了这一点,但TypeScript没识别到,你可以临时用类型断言来告诉它:
const { mutate: createExample } = api.example.createExample.useMutation({ onSuccess: (data) => { setItems((prev) => [...prev, data as Example]); }, });
当然,最好的方式是完善API的类型定义,让createExample的返回值明确为Example,这样就不需要断言了。
2. 提前过滤掉undefined的情况
如果业务上确实存在data可能为空的场景,那就在更新状态前先做判断:
const { mutate: createExample } = api.example.createExample.useMutation({ onSuccess: (data) => { // 如果data不存在,直接返回,不更新状态 if (!data) return; setItems((prev) => [...prev, data]); }, });
这样TypeScript就会知道,进入setItems逻辑时data一定是Example类型,数组类型就匹配了。
3. 调整useState的类型(不推荐)
如果你的业务逻辑允许数组里存在undefined,那可以把主组件的useState类型改成包含undefined的联合类型:
const [items, setItems] = useState<(Example | undefined)[]>([]);
但这显然不符合你要存有效Example的需求,所以只作为极端情况的备选。
额外的小优化
我还注意到你主组件里的列表渲染逻辑可以简化一下:
// 优化后的写法,加了必须的key属性 {items.length > 0 ? ( <ul> {items.map((example) => ( <li key={example.id}> {example.text}{" "} <button onClick={() => deleteExample({ id: example.id })}>X</button> </li> ))} </ul> ) : null}
另外,现在Next.js里写组件可以不用FC类型,直接写const Form = ({ setItems }: FormProps) => { ... }就好,代码更清爽。
内容的提问来源于stack exchange,提问作者NickP

