Next 13 + React 18 构建时createContext类型错误排查与解决
Next 13 + React 18 上下文类型检查错误修复方案
错误信息
Type error: Type 'OmitWithTag<typeof import("....calculator/page"), "metadata" | "default" | "config" | "generateStaticParams" | "revalidate" | ... 6 more ... | "generateMetadata", "">' does not satisfy the constraint '{ [x: string]: never; }'. Property 'ActiveTabContext' is incompatible with index signature. Type 'Context<ActiveTab>' is not assignable to type 'never'.
问题代码
type ActiveTab = [string, Dispatch<SetStateAction<string>>] type SelectValues = [IApiCostsState[], Dispatch<SetStateAction<IApiCostsState[]>>] type SumState = [number, Dispatch<SetStateAction<number>>] type HelperState = [Helper, Dispatch<SetStateAction<Helper>>] export const ActiveTabContext = createContext<ActiveTab>(['', () => { }]) as Context<ActiveTab>;; export const SelectValuesContext = createContext<SelectValues>([setInitial(Object.keys(_apiCostsMock)[0]), () => { }]); export const TotalSumContext = createContext<SumState>([0, () => { }]); export const HelperContext = createContext<HelperState>([{index:-1}, () => { }]); export const WindowWidthContext = createContext<number>(0); export const ScrollElementContext = createContext<MutableRefObject<HTMLDivElement | null> | null>(null);
原因分析
Next.js 13的App Router对page.tsx和layout.tsx的导出有严格约束:这些文件只能导出Next官方允许的内容(比如默认组件、metadata、generateStaticParams等)。如果在这些文件中导出Context这类自定义变量,TypeScript会触发类型检查错误——因为Next要求这些文件的额外导出必须符合{ [x: string]: never; }(即不能有任何额外导出),而Context类型显然不满足这个要求。
修复步骤
- 分离Context定义:创建单独的文件存放Context(比如
app/calculator/contexts.ts),把所有Context的类型定义和创建逻辑移到这个文件中:
// app/calculator/contexts.ts import { createContext, Dispatch, SetStateAction, MutableRefObject } from 'react'; import { IApiCostsState, Helper } from './your-types-file'; // 替换为你的类型文件路径 import { setInitial } from './your-utils-file'; // 替换为你的工具函数路径 import _apiCostsMock from './your-mock-file'; // 替换为你的mock文件路径 type ActiveTab = [string, Dispatch<SetStateAction<string>>] type SelectValues = [IApiCostsState[], Dispatch<SetStateAction<IApiCostsState[]>>] type SumState = [number, Dispatch<SetStateAction<number>>] type HelperState = [Helper, Dispatch<SetStateAction<Helper>>] export const ActiveTabContext = createContext<ActiveTab>(['', () => { }]); export const SelectValuesContext = createContext<SelectValues>([setInitial(Object.keys(_apiCostsMock)[0]), () => { }]); export const TotalSumContext = createContext<SumState>([0, () => { }]); export const HelperContext = createContext<HelperState>([{index:-1}, () => { }]); export const WindowWidthContext = createContext<number>(0); export const ScrollElementContext = createContext<MutableRefObject<HTMLDivElement | null> | null>(null);
- 清理page/layout文件的导出:在page.tsx或layout.tsx中,只保留Next允许的导出项(比如默认组件、metadata),通过import导入需要的Context:
// app/calculator/page.tsx import { ActiveTabContext, SelectValuesContext } from './contexts'; export default function CalculatorPage() { // 使用Context的逻辑 return <div>...</div>; } // 只保留Next允许的导出,比如metadata(如果需要) export const metadata = { title: '计算器页面', };
- 移除不必要的类型断言:原代码中
as Context<ActiveTab>是多余的,createContext<T>会自动返回Context<T>类型,无需额外断言;同时注意移除多余的分号(比如as Context<ActiveTab>;;中的两个分号)。
内容的提问来源于stack exchange,提问作者Илья Дуванов
相关产品推荐
相关产品推荐

