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

Next.js(TypeScript)中withAuth高阶组件最优实现及ESLint问题解决

问题描述

我有一个使用next-auth包的Next.js应用,需要创建一个包裹业务组件、用于判断用户会话状态的高阶组件(HOC),同时用ESLint校验代码。初始代码使用any类型触发了ESLint警告,改成泛型<P extends {}>后又触发了@typescript-eslint/ban-types规则错误,想请教不确定组件类型时编写这类HOC的最佳方式。

初始代码:

import { useRouter } from 'next/navigation';
import { useSession } from 'next-auth/react';

type Props = any;

const withAuth = (Component: React.ComponentType<Props>) => {
  const Auth = (props: Props) => {
    const router = useRouter();
    const { status } = useSession({
      required: true,
      onUnauthenticated() {
        router.push('/welcome/login');
      },
    });

    if (status === 'loading') {
      return 'Loading ...';
    }

    return <Component {...props} />;
  };
  return Auth;
};

export default withAuth;

修改后的代码片段(触发ESLint错误):

const withAuth = <P extends {}>(Component: React.ComponentType<P>) => {
  const Auth = (props: P) => {

ESLint报错信息:

Error: Don't use `{}` as a type. `{}` actually means "any non-nullish value".
- If you want a type meaning "any object", you probably want `object` instead.
- If you want a type meaning "any value", you probably want `unknown` instead.
- If you want a type meaning "empty object", you probably want `Record<string, never>` instead.
- If you really want a type meaning "any non-nullish value", you probably want `NonNullable<unknown>` instead.  @typescript-eslint/ban-types

解决方案

问题核心是泛型约束的类型选择错误,以下是两种可行的修正方式:

方案1:使用无约束泛型

直接去掉泛型的extends {}约束,React.ComponentType<P>本身可接受任意合法的props类型,无需额外约束:

import { useRouter } from 'next/navigation';
import { useSession } from 'next-auth/react';
import type { ComponentType } from 'react';

const withAuth = <P>(Component: ComponentType<P>) => {
  const Auth = (props: P) => {
    const router = useRouter();
    const { status } = useSession({
      required: true,
      onUnauthenticated() {
        router.push('/welcome/login');
      },
    });

    if (status === 'loading') {
      return 'Loading ...';
    }

    return <Component {...props} />;
  };
  return Auth;
};

export default withAuth;

方案2:约束为对象类型(推荐)

如果希望明确props是键值对结构(符合React组件props的常规形态),可以用Record<string, unknown>替代{},精准表达“键为字符串、值为任意类型的对象”:

import { useRouter } from 'next/navigation';
import { useSession } from 'next-auth/react';
import type { ComponentType } from 'react';

const withAuth = <P extends Record<string, unknown>>(Component: ComponentType<P>) => {
  const Auth = (props: P) => {
    const router = useRouter();
    const { status } = useSession({
      required: true,
      onUnauthenticated() {
        router.push('/welcome/login');
      },
    });

    if (status === 'loading') {
      return 'Loading ...';
    }

    return <Component {...props} />;
  };
  return Auth;
};

export default withAuth;

说明

  • 方案1的无约束泛型:完全适配所有组件的props类型,包括空props场景,既避免了any的类型不安全问题,也不会触发ESLint规则。
  • 方案2的Record<string, unknown>:规避了{}类型的歧义(ESLint禁止{}是因为它实际代表“非null/undefined的任意值”,而非空对象),同时符合组件props的常规结构。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 21:18:17