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
相关产品推荐
相关产品推荐

