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

Next.js 13 + TypeScript构建时出现Type is not assignable to type 'never'错误

Next.js 13 + TypeScript 构建类型错误解决方案

问题场景

构建基于Next.js 13、TypeScript和ESLint的项目时,控制台抛出如下类型错误:

.next/types/app/facebook/page.ts:8:13
Type error: Type 'OmitWithTag<typeof import("D:/projects/abkh24/client/app/facebook/page"), "metadata" | "default" | "config" | "generateStaticParams" | "revalidate" | "dynamic" | "dynamicParams" | "fetchCache" | "preferredRegion" | "runtime" | "maxDuration" | "generateMetadata", "">' does not satisfy the constraint '{ [x: string]: never; }'.
Property 'fetchUser' is incompatible with index signature.
Type '(id: string, token: string) => Promise<UserData | null>' is not assignable to type 'never'.

6 |
7 | // Check that the entry is a valid entry

8 | checkFields<Diff<{
| ^
9 | default: Function
10 | config?: {}
11 | generateStaticParams?: Function
error Command failed with exit code 1.

错误涉及包含fetchUser函数的页面组件,该函数用于调用Facebook Graph API获取用户信息,组件代码如下:

"use client";

import { Box } from "@mui/material";
import React, { useState, useEffect, Fragment } from "react";
import Spinner from "@/components/Spinner";
import { useIsMount } from "@/misc/tools";
import { useAppDispatch, useAppSelector } from "@/store/hooks";
import { setAuth } from "@/store/authSlice";
import { RootState } from "@/store";
import { redirect } from "next/navigation";

const ACCESS_TOKEN = process.env.NEXT_PUBLIC_ACCESS_TOKEN;

type UserData = {
  avatar: string;
  category: string;
  name: string;
};

function stringBetweenStrings(
  startStr: string,
  endStr: string,
  str: string,
): string {
  const pos = str.indexOf(startStr) + startStr.length;
  return str.substring(pos, str.indexOf(endStr, pos));
}

export const fetchUser = async (
  id: string,
  token: string,
): Promise<UserData | null> => {
  try {
    const response = await fetch(
      `https://graph.facebook.com/${id}?fields=name,picture&access_token=${token}`,
    );
    const data = await response.json();
    return {
      avatar: data.picture.data.url,
      category: "facebook",
      name: data.name,
    };
  } catch (error) {
    console.error("Error fetching user data", error);
    return null;
  }
};

export default function page() {
  const { name, avatar, category } = useAppSelector(
    (state: RootState) => state.auth,
  );
  const dispatch = useAppDispatch();
  const isMount = useIsMount();
  const [facebookId, setFacebookId] = useState<string>('');
  useEffect(() => {
    const myUrl = window.location.href;
    setFacebookId(stringBetweenStrings("id=", "#_=_", myUrl));
  }, []);

  useEffect(() => {
    (async () => {
      if (!isMount && ACCESS_TOKEN) {
        const data: UserData | null = await fetchUser(facebookId, ACCESS_TOKEN);
        dispatch(setAuth({ ...data, id: facebookId }));
      }
    })();
  }, [facebookId]);

  useEffect(() => {
    if (!isMount && category) {
      redirect("/");
    }
  }, [category]);

  return (
    <Fragment>
      <Box
        sx={{
          position: "fixed",
          zIndex: 111,
          top: 0,
          left: 0,
          height: "100vh",
          width: "100%",
          display: "flex",
          justifyContent: "center",
          alignItems: "center",
          background: "#fff",
        }}
      ></Box>
      <Spinner />
    </Fragment>
  );
}

问题原因

Next.js 13的App Router对页面文件的导出有严格限制:页面文件仅允许导出default组件、metadata、generateMetadata、generateStaticParams等官方指定内容,不能导出自定义函数(如这里的fetchUser)。非规范导出会触发类型检查报错。

解决方法

方法1:将函数改为文件私有函数

移除fetchUser和stringBetweenStrings的export关键字,让它们成为页面文件内部的私有函数:

// 移除export,改为私有函数
const fetchUser = async (
  id: string,
  token: string,
): Promise<UserData | null> => {
  // 函数逻辑保持不变
};

const stringBetweenStrings = (
  startStr: string,
  endStr: string,
  str: string,
): string => {
  // 函数逻辑保持不变
};

方法2:抽离函数到工具文件

若函数需要复用,将其移至独立工具文件(如@/utils/facebook.ts),再在页面组件中导入:

  1. 创建@/utils/facebook.ts:
export type UserData = {
  avatar: string;
  category: string;
  name: string;
};

export const fetchUser = async (
  id: string,
  token: string,
): Promise<UserData | null> => {
  try {
    const response = await fetch(
      `https://graph.facebook.com/${id}?fields=name,picture&access_token=${token}`,
    );
    const data = await response.json();
    return {
      avatar: data.picture.data.url,
      category: "facebook",
      name: data.name,
    };
  } catch (error) {
    console.error("Error fetching user data", error);
    return null;
  }
};

export const stringBetweenStrings = (
  startStr: string,
  endStr: string,
  str: string,
): string => {
  const pos = str.indexOf(startStr) + startStr.length;
  return str.substring(pos, str.indexOf(endStr, pos));
};
  1. 在页面组件中导入:
"use client";

import { Box } from "@mui/material";
import React, { useState, useEffect, Fragment } from "react";
import Spinner from "@/components/Spinner";
import { useIsMount } from "@/misc/tools";
import { useAppDispatch, useAppSelector } from "@/store/hooks";
import { setAuth } from "@/store/authSlice";
import { RootState } from "@/store";
import { redirect } from "next/navigation";
// 导入工具函数
import { fetchUser, stringBetweenStrings, UserData } from "@/utils/facebook";

const ACCESS_TOKEN = process.env.NEXT_PUBLIC_ACCESS_TOKEN;

export default function page() {
  // 组件逻辑保持不变
}

修改后页面文件仅保留规范导出内容,类型检查将通过,构建错误消失。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 09:44:54