React组件JSX中如何基于isSeller转换用户变量的TS类型?
解决TypeScript中根据isSeller字段区分用户类型的问题
问题背景
在React个人资料页组件中,用户分为普通客户(customer)和卖家(seller)两种角色,卖家拥有ico、firmName等额外属性。当API返回的用户对象isSeller为true时,TypeScript无法自动识别该用户为TSeller类型,导致访问firmName等属性时提示“属性在TCustomer类型中不存在”的错误。
类型定义
import type { TDeliveryInformation } from '../../pages/OrderCreate' export interface TCustomer { firstName: string lastName: string email: string phone: string bornDate: string isSeller: boolean deliveryInformation: TDeliveryInformation } export interface TSeller extends TCustomer { ico: string firmName: string // ...其他卖家专属属性 }
现有查询与组件代码
查询代码
import { useQuery } from '@tanstack/react-query' import { api } from '../../index' import type { TCustomer, TSeller } from '../../../@types/user/user' const useMeQuery = () => useQuery({ queryKey: ['me'], queryFn: () => api().get(`me`).json<TCustomer | TSeller>(), }) export default useMeQuery
组件示例
import { NavLink, Outlet } from 'react-router-dom' import { useTranslation } from 'react-i18next' import useMeQuery from '../../api/query/user/useMeQuery' import Loader from '../../components/reusableComponents/Loader' const User = () => { const { t } = useTranslation() const { data: userInfo, isLoading: userBeingLoaded, isError: userError, } = useMeQuery() if (userBeingLoaded) return <Loader /> if (userError) return 'Error happened' if (!userInfo) return null return ( <div className="wrapper-width mx-auto mt-10"> <div className="flex"> <div> <h2> {userInfo.isSeller {/* 此处TypeScript无法识别userInfo为TSeller类型 */} ? userInfo.firmName : `${userInfo.firstName} ${userInfo.lastName}`} </h2> </div> <Outlet /> </div> </div> ) } export default User
解决方案
方案1:使用类型守卫函数
定义一个类型守卫函数,帮助TypeScript缩小用户类型范围:
// 可放在类型定义文件或组件文件中 function isSeller(user: TCustomer | TSeller): user is TSeller { return user.isSeller; }
在组件中使用该函数进行类型判断:
<h2> {isSeller(userInfo) ? userInfo.firmName : `${userInfo.firstName} ${userInfo.lastName}`} </h2>
方案2:修改类型为区分联合类型(Discriminated Union)
调整类型定义,将isSeller设为区分字段,让TypeScript自动识别类型:
import type { TDeliveryInformation } from '../../pages/OrderCreate' export interface TCustomer { firstName: string lastName: string email: string phone: string bornDate: string isSeller: false // 明确普通用户的isSeller为false deliveryInformation: TDeliveryInformation } export interface TSeller extends Omit<TCustomer, 'isSeller'> { isSeller: true // 明确卖家的isSeller为true ico: string firmName: string // ...其他卖家专属属性 }
此时无需额外函数,直接通过userInfo.isSeller判断即可让TypeScript自动推断类型:
<h2> {userInfo.isSeller ? userInfo.firmName : `${userInfo.firstName} ${userInfo.lastName}`} </h2>
方案3:类型断言(快速但不够安全)
如果确定isSeller为true时用户一定是TSeller,可使用类型断言快速解决,但这种方式跳过了TypeScript的类型检查,仅适合临时场景:
<h2> {userInfo.isSeller ? (userInfo as TSeller).firmName : `${userInfo.firstName} ${userInfo.lastName}`} </h2>
内容的提问来源于stack exchange,提问作者Tomáš Kretek
相关产品推荐
相关产品推荐

