OpenAI版本更新致Next.js ChatGPT类SaaS应用类型错误求助
解决Next.js + OpenAI API类型错误问题
问题背景
我使用Next.js和OpenAI API开发首个类ChatGPT的SaaS应用,跟随YouTube教程完成了大部分开发工作,但因OpenAI版本更新遭遇两个类型错误,尝试Stack Overflow、GitHub等平台的方案仍未解决。
报错代码与信息
page.tsx核心代码
"use client"; import axios from "axios"; import * as z from "zod"; import { Bot } from "lucide-react"; import { useForm } from "react-hook-form"; import { Heading } from "@/components/heading"; import { Form, FormControl, FormField, FormItem } from "@/components/ui/form"; import { Empty } from "@/components/empty"; import { Loader } from "@/components/loader"; import { formSchema } from "./constants"; import { zodResolver } from "@hookform/resolvers/zod"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { useRouter } from "next/navigation"; import { useState } from "react"; import { ChatCompletionMessageParam } from "openai/resources/chat/completions"; import { cn } from "@/lib/utils"; import { UserAvatar } from "@/components/user-avatar"; import { BotAvatar } from "@/components/bot-avatar"; const ConversationPage = () => { const router = useRouter(); const [messages, setMessages] = useState<ChatCompletionMessageParam[]>([]); const form = useForm<z.infer<typeof formSchema>>({ resolver: zodResolver(formSchema), defaultValues: { prompt: "", }, }); const isLoading = form.formState.isSubmitting; const onSubmit = async (values: z.infer<typeof formSchema>) => { try { const userMessage: ChatCompletionMessageParam = { role: "user", content: values.prompt, }; const newMessages = [...messages, userMessage]; const response = await axios.post("/api/sociagpt", { messages: newMessages, }); setMessages((current) => [...current, userMessage, response.data]); form.reset(); } catch (error: any) { console.log(error); } finally { router.refresh(); } }; return ( <div> <Heading title="SociaGPT" description="An advance chatbot for social media marketing strategies." icon={Bot} iconColor="text-violet-500" bgColor="bg-violet-500/10" /> <div className="px-4 lg:px-8"> <div> <Form {...form}> <form onSubmit={form.handleSubmit(onSubmit)} className="rounded-lg border w-full p-4 px-3 md:px-6 focus-within:shadow-sm grid grid-cols-12 gap-2 " > <FormField name="prompt" render={({ field }) => ( <FormItem className="col-span-12 lg:col-span-10"> <FormControl className="m-0 p-0"> <Input className="border-0 outline-none focus-visible:ring-0 focus-visible:ring-transparent" disabled={isLoading} placeholder="What is the best marketing strategy to follow in 2024?" {...field} /> </FormControl> </FormItem> )} /> <Button className="col-span-12 lg:col-span-2 w-full" disabled={isLoading} > Ask Socia </Button> </form> </Form> </div> <div className="space-y-4 mt-4"> {isLoading && ( <div className="p-8 rounded-lg w-full flex items-center justify-center bg-muted"> <Loader /> </div> )} {messages.length === 0 && !isLoading && ( <Empty label="No conversation started yet." /> )} <div className="flex flex-col-reverse gap-y-4"> {messages.map((message) => ( <div key={message.content} className={cn( "p-8 w-fiull flex items-start gap-x-8 rounded-lg", message.role === "user" ? "bg-white border border-black/10" : "bg-muted" )} > {message.role === "user" ? <UserAvatar /> : <BotAvatar />} <p className="text-sm">{message.content}</p> </div> ))} </div> </div> </div> </div> ); }; export default ConversationPage;
报错片段与错误信息
<div key={message.content} className={cn( "p-8 w-fiull flex items-start gap-x-8 rounded-lg", message.role === "user" ? "bg-white border border-black/10" : "bg-muted" )} > {message.role === "user" ? <UserAvatar /> : <BotAvatar />} <p className="text-sm">{message.content}</p> </div>
错误提示:
Type 'string | ChatCompletionContentPart[] | null' is not assignable to type 'Key | null | undefined'.Type 'string | ChatCompletionContentPart[] | null' is not assignable to type 'ReactNode'.
route.ts代码
import { auth } from "@clerk/nextjs"; import { NextResponse } from "next/server"; import { OpenAI } from "openai"; const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY, }); export async function POST(req: Request) { try { const { userId } = auth(); const body = await req.json(); const { messages } = body; if (!userId) { return new NextResponse("Unaothorized", { status: 401 }); } if (!openai.apiKey) { return new NextResponse("OpenAI API Key not configured", { status: 500 }); } if (!messages) { return new NextResponse("Messages are required", { status: 400 }); } const response = await openai.chat.completions.create({ model: "gpt-3.5-turbo", messages, }); return NextResponse.json(response.choices[0].message); } catch (error) { console.log("[CONVERSATION_ERROR]", error); return new NextResponse("Internal error", { status: 500 }); } }
解决方案
错误根源是OpenAI新版API中ChatCompletionMessageParam的content字段类型为string | ChatCompletionContentPart[] | null,与React对key和渲染内容的类型要求不匹配,以下是具体修复步骤:
1. 修复React元素的Key问题
React要求key必须是字符串、数字等简单类型,不能直接使用message.content(可能是数组或null),改用角色+索引作为唯一标识:
{messages.map((message, index) => ( <div key={`${message.role}-${index}`} // ... 其他属性 > // ... 内部内容 </div> ))}
2. 修复消息内容渲染问题
需要对message.content进行类型判断,转换成React可渲染的字符串:
<p className="text-sm"> {typeof message.content === "string" ? message.content : Array.isArray(message.content) ? message.content.map(part => part.text).join('') : "无内容"} </p>
3. (可选)优化消息状态类型
自定义更严格的消息类型,避免后续类型问题:
// 在page.tsx顶部添加自定义类型 interface ChatMessage { role: 'user' | 'assistant'; content: string; } // 修改useState的类型 const [messages, setMessages] = useState<ChatMessage[]>([]); // 修改onSubmit中的消息处理逻辑 const onSubmit = async (values: z.infer<typeof formSchema>) => { try { const userMessage: ChatMessage = { role: "user", content: values.prompt, }; const newMessages = [...messages, userMessage]; const response = await axios.post("/api/sociagpt", { messages: newMessages, }); // 处理API返回的助手消息,确保content为字符串 const assistantMessage: ChatMessage = { role: response.data.role, content: typeof response.data.content === "string" ? response.data.content : Array.isArray(response.data.content) ? response.data.content.map(part => part.text).join('') : "无内容" }; setMessages((current) => [...current, userMessage, assistantMessage]); form.reset(); } catch (error: any) { console.log(error); } finally { router.refresh(); } };
4. 修正拼写错误
原代码中w-fiull是拼写错误,改为w-full:
className={cn( "p-8 w-full flex items-start gap-x-8 rounded-lg", // 修正拼写 // ... 其他样式 )}
内容的提问来源于stack exchange,提问作者Muhammadh Rifath
相关产品推荐
相关产品推荐

