传递currentUser至UserMenu组件时的TypeScript类型错误求助
报错信息
Type '{ createdAt: string; updatedAt: string; emailVerified: string | null; id: string; name: string | null; email: string | null; image: string | null; hashedPassword: string | null; role: Role; } | null | undefined' is not assignable to type 'SafeUser | null'. Type 'undefined' is not assignable to type 'SafeUser | null'.ts(2322) UserMenu.tsx(13, 5): 预期类型来自属性'currentUser',该属性在类型'IntrinsicAttributes & UserMenuProps'上声明
问题核心
getCurrentUser返回的currentUser被TypeScript推断为SafeUser | null | undefined类型,但UserMenu组件的currentUser属性仅接受SafeUser | null,不允许undefined。尽管getCurrentUser的返回类型声明为Promise<SafeUser | null>,但在异步组件场景下,TypeScript会额外推断出undefined的可能性。
解决方案
方案1:传递时将undefined转为null
在MainComponent中使用空值合并运算符,把undefined转为null,匹配组件要求的类型:
// MainComponent.tsx <UserMenu currentUser={currentUser ?? null} />
方案2:显式标注currentUser类型
给currentUser指定明确类型,消除TypeScript的额外推断:
// MainComponent.tsx const currentUser: SafeUser | null = await getCurrentUser(); // 正常传递即可 <UserMenu currentUser={currentUser} />
方案3:调整UserMenuProps兼容undefined
如果业务逻辑允许,修改组件属性类型,直接兼容undefined:
// UserMenu.tsx interface UserMenuProps { currentUser: SafeUser | null | undefined; }
组件内部的currentUser ? ... : ...逻辑会自动处理undefined(和null逻辑一致),无需额外修改。
验证要点
- 确保
getCurrentUser的所有分支都返回SafeUser或null(当前代码已满足) - 组件接收的
currentUser类型与传递的类型完全匹配
相关代码参考
MainComponent.tsx(方案1实现)
const MainComponent = async () => { const currentUser = await getCurrentUser(); return ( <div className="sticky top-0 w-full bg-slate-200 z-30 shadow-sm"> <div className="py-4 border-b-[1px]"> <Container> <div className="flex items-center justify-between gap-3 md-gap-0"> <Link href="/" className={`${redressed.className} font-bold text-2xl`}>E-Shop</Link> <div className="hidden md:block">搜索</div> <div className="flex items-center gap-8 md:gap-12"> <CartCount /> <UserMenu currentUser={currentUser ?? null} /> </div> </div> </Container> </div> </div> ); }; export default MainComponent;
UserMenu.tsx(方案3实现)
import React, { useState, useCallback } from 'react'; import Link from 'next/link'; import { AiFillCaretDown } from 'react-icons/ai'; import Avatar from './Avatar'; import MenuItem from './MenuItem'; import BackDrop from './BackDrop'; import Container from './Container'; import CartCount from './CartCount'; import { signOut } from 'next-auth/react'; import { redressed } from '@/styles/fonts'; interface UserMenuProps { currentUser: SafeUser | null | undefined; } const UserMenu: React.FC<UserMenuProps> = ({ currentUser }) => { const [isOpen, setIsOpen] = useState(false); const toggleOpen = useCallback(() => { setIsOpen((prev) => !prev); }, []); return ( <> <div className="relative z-30"> <div onClick={toggleOpen} className="p-2 border-[1px] border-slate-400 flex flex-row items-center gap-1 rounded-full cursor-pointer hover:shadow-md transition text-slate-700" > <Avatar /> <AiFillCaretDown /> </div> {isOpen && ( <div className="absolute rounded-md shadow-md w-[170px] bg-white overflow-hidden right-0 top-12 text-sm flex flex-col cursor-pointer"> {currentUser ? ( <div> <Link href="/orders"> <MenuItem onClick={toggleOpen}>你的订单</MenuItem> </Link> <Link href="/admin"> <MenuItem onClick={toggleOpen}>管理员面板</MenuItem> </Link> <hr /> <MenuItem onClick={() => { toggleOpen(); signOut(); }} > 退出登录 </MenuItem> </div> ) : ( <div> <Link href="/login"> <MenuItem onClick={toggleOpen}>登录</MenuItem> </Link> <Link href="/register"> <MenuItem onClick={toggleOpen}>注册</MenuItem> </Link> </div> )} </div> )} </div> {isOpen && <BackDrop onClick={toggleOpen} />} </> ); }; export default UserMenu;
getCurrentUser.ts
import { authOptions } from "@/pages/api/auth/[...nextauth]"; import { getServerSession } from "next-auth"; import prisma from "@/libs/prismadb"; export async function getSession() { return await getServerSession(authOptions); } export async function getCurrentUser(): Promise<SafeUser | null> { try { const session = await getSession(); if (!session?.user?.email) { return null; } const currentUser = await prisma.user.findUnique({ where: { email: session.user.email, }, }); if (!currentUser) { return null; } return { ...currentUser, createdAt: currentUser.createdAt.toISOString(), updatedAt: currentUser.updatedAt.toISOString(), emailVerified: currentUser.emailVerified?.toISOString() || null, }; } catch (error: any) { console.log(error); return null; } }
SafeUser类型定义
import { User } from "@prisma/client"; export type SafeUser = Omit<User, "createdAt" | "updatedAt" | "emailVerified"> & { createdAt: string; updatedAt: string; emailVerified: string | null; };
内容的提问来源于stack exchange,提问作者Viteazu Mihai
相关产品推荐
相关产品推荐

