You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>

错误提示:

  1. Type 'string | ChatCompletionContentPart[] | null' is not assignable to type 'Key | null | undefined'.
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 19:17:34