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

Next.js 13执行npx run build时触发Type Error错误求助

解决Next.js构建时客户端组件Props类型错误

错误信息

Type error: Type 'OmitWithTag<{ user: User; cupons: Cupon[]; }, keyof LayoutProps, "default">' does not satisfy the constraint '{ [x: string]: never; }'.
  Property 'user' is incompatible with index signature.
    Type 'User' is not assignable to type 'never'.

  24 | 
  25 | // Check the prop type of the entry function
> 26 | checkFields<Diff<LayoutProps, FirstArg<TEntry['default']>, 'default'>>()
     |             ^
  27 | 
  28 | // Check the arguments and return type of the generateMetadata function
  29 | if ('generateMetadata' in entry) {

相关代码

components/layout.tsx

"use client";
import React from "react";
import Container from "@/components/ui/container";
import NextImage from "next/image";
import moment from "moment";
import { User, Cupon } from "@/types";

const Layout = ({ user, cupons }: { user: User; cupons: Cupon[] }) => {
  const userCoupons = user.cupons
    .map((userCupon: any) => {
      return cupons.find((cupon: any) => cupon.id === userCupon.cuponId);
    })
    .filter(Boolean);
  return (
    <Container>
      <div className="flex flex-col mt-20 p-3 space-y-3">
        <h1 className="text-3xl font-bold text-left">User Page</h1>
        <div className="flex items-start justify-start gap-7">
          <NextImage
            alt="user_img"
            src={user.image}
            width={400}
            height={400}
            className="rounded-md"
          />
          <div className="flex flex-col items-start justify-start pt-3">
            <h1 className="text-2xl font-semibold text-left">
              Personal Information
            </h1>
            <hr className="w-full text-black" />

            <h1 className="text-[17px] font-semibold text-left text-slate-900 opacity-75 mt-3">
              Name: {user.name}
            </h1>
            <h1 className="text-[17px] font-semibold text-left text-slate-900 opacity-75 mt-3">
              Username: {`@${user.name.split(" ").join("_").toLowerCase()}`}
            </h1>
            <h1 className="text-[17px] font-semibold text-left text-slate-900 opacity-75">
              Email: {user.email}
            </h1>
            <h1 className="text-[17px] font-semibold text-left text-slate-900 opacity-75">
              Cupons Claimed: {user.cupons.length}
            </h1>
            <h1 className="text-[17px] font-semibold text-left text-slate-900 opacity-75">
              User Since: {moment(user.createdAt).format("DD/MM/YYYY")}
            </h1>
            <br />
            <br />
            <h1 className="text-2xl font-semibold text-left">
              Accoutnt Security
            </h1>
            <hr className="w-full text-black" />
            <h1 className="text-[17px] font-semibold text-left text-slate-900 opacity-75 mt-3">
              Auth provider: {user.accounts[0].provider}
            </h1>
            <h1 className="text-[17px] font-semibold text-left text-slate-900 opacity-75">
              User ID: {user.accounts[0].id}
            </h1>
            <h1 className="text-[17px] font-semibold text-left text-slate-900 opacity-75">
              Auth type: {user.accounts[0].type}
            </h1>
          </div>
          <div className="flex flex-col items-start justify-start pt-3">
            <h1 className="text-2xl font-semibold text-left">Cupons Claimed</h1>
            <hr className="w-full text-black" />
            {userCoupons.map((cupon: any) => (
              <div
                key={cupon.id}
                className="flex items-start justify-start mt-3 bg-gray-300 bg-opacity-40 p-3 rounded-md w-full"
              >
                <h1 className="text-[17px] font-semibold text-left text-slate-900 opacity-75">
                  Cupon ID: {cupon.id}
                </h1>
                <h1 className="text-[17px] font-semibold text-left text-slate-900 opacity-75">
                  Cupon Code: {cupon.code}
                </h1>
                <h1 className="text-[17px] font-semibold text-left text-slate-900 opacity-75">
                  Cupon Value: {cupon.value}
                </h1>
              </div>
            ))}
          </div>
        </div>
      </div>
    </Container>
  );
};

export default Layout;

app/[storeId]/[userId]/page.tsx

import React from "react";
import Layout from "../components/layout";
import getCupons from "@/actions/get-cupons";

interface UserPageProps {
  params: {
    userId: string;
    storeId: string;
  };
}

const UserPage: React.FC<UserPageProps> = async ({ params }) => {
  const user = (await getUser(params.userId)) as any;
  const cupons = (await getCupons(params.storeId)) as any;
  const { userData } = JSON.parse(user) as any;
  return (
    <div>
      <Layout user={userData} cupons={cupons} />
    </div>
  );
};

export default UserPage;

解决方案

1. 避免组件命名冲突

Next.js对App Router中的layout.tsx有特殊类型约定,即使组件在components目录,命名为Layout也可能触发内部类型检查混淆。将组件重命名为UserLayout:

  • 修改layout.tsx为user-layout.tsx,并更新组件定义:
    "use client";
    import React from "react";
    import Container from "@/components/ui/container";
    import NextImage from "next/image";
    import moment from "moment";
    import { User, Cupon } from "@/types";
    
    interface UserLayoutProps {
      user: User;
      cupons: Cupon[];
    }
    
    const UserLayout = ({ user, cupons }: UserLayoutProps) => {
      // 原有逻辑保持不变
    };
    
    export default UserLayout;
    
  • 更新page.tsx中的导入和使用:
    import React from "react";
    import UserLayout from "../components/user-layout";
    import getCupons from "@/actions/get-cupons";
    import getUser from "@/actions/get-user";
    
    interface UserPageProps {
      params: {
        userId: string;
        storeId: string;
      };
    }
    
    const UserPage: React.FC<UserPageProps> = async ({ params }) => {
      const userRes = await getUser(params.userId);
      const { userData } = JSON.parse(userRes) as { userData: User };
      const cupons = await getCupons(params.storeId) as Cupon[];
    
      return (
        <div>
          <UserLayout user={userData} cupons={cupons} />
        </div>
      );
    };
    
    export default UserPage;
    

2. 严格匹配Props类型,移除宽泛断言

去掉代码中as any的宽泛断言,明确指定类型,确保传递给客户端组件的Props与组件定义完全一致:

  • 确保getUser返回结果解析为{ userData: User }结构
  • 确保getCupons返回类型为Cupon[]

3. 检查基础类型定义

确认@/types中的User和Cupon类型无循环引用或不可序列化字段(如函数、Symbol):

// @/types/index.ts
export interface User {
  id: string;
  name: string;
  email: string;
  image: string;
  createdAt: string;
  cupons: { cuponId: string }[];
  accounts: {
    provider: string;
    id: string;
    type: string;
  }[];
}

export interface Cupon {
  id: string;
  code: string;
  value: string | number;
}

4. 升级Next.js版本

若以上方法无效,尝试升级Next.js到最新稳定版,该错误可能是旧版本的类型检查bug导致。

内容的提问来源于stack exchange,提问作者Updater XD

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 10:54:56