React组件中API响应类型化与条件渲染的TypeScript报错问题
你遇到的这个报错其实是TypeScript类型窄化的一个常见坑——当你把类型判断的结果存在单独的布尔变量里时,TypeScript没办法把这个变量和原对象的类型关联起来。
具体来说,你预先定义的isPostalAddress和isPackstationAddress只是普通的布尔值,TypeScript不会记住它们是通过检查deliveryAddress.type得到的。所以当你在JSX里用这些变量做条件判断时,TypeScript无法确定此时deliveryAddress的具体类型,它仍然会把deliveryAddress当成PostalAddress | PackstationAddress的联合类型,自然会报错说street/packstation这些属性可能在另一种类型中不存在。
下面给你几种实用的解决办法:
1. 直接在JSX条件中做类型判断(最简单直接)
把类型检查逻辑直接写在JSX的条件表达式里,这样TypeScript能实时对deliveryAddress进行类型窄化:
return ( <div className="App"> {props.deliveryAddress.type === AddressType.postal && ( <> <h2>Street: {props.deliveryAddress.street}</h2> <h2>Street Number: {props.deliveryAddress.streetNumber}</h2> </> )} {props.deliveryAddress.type === AddressType.packstation && ( <> <h2>Packstation: {props.deliveryAddress.packstation}</h2> <h2>Post Number: {props.deliveryAddress.postNumber}</h2> </> )} </div> );
2. 自定义类型守卫函数(适合需要复用判断逻辑的场景)
如果希望保留预先判断的逻辑,可以写一个类型守卫函数,明确告诉TypeScript判断结果对应的类型:
首先在types.ts中添加守卫函数:
export function isPostalAddress(address: DeliveryAddress): address is PostalAddress { return address.type === AddressType.postal; } export function isPackstationAddress(address: DeliveryAddress): address is PackstationAddress { return address.type === AddressType.packstation; }
然后在组件中使用:
const DeliveryAddressDisplay = (props: Props) => { const address = props.deliveryAddress; const isPostal = isPostalAddress(address); const isPackstation = isPackstationAddress(address); return ( <div className="App"> {isPostal && <h2>Street: {address.street}</h2>} {isPackstation && <h2>Packstation: {address.packstation}</h2>} {isPostal && <h2>Street Number: {address.streetNumber}</h2>} {isPackstation && <h2>Post Number: {address.postNumber}</h2>} </div> ); };
类型守卫函数的关键在于返回值类型address is PostalAddress,它会告诉TypeScript:如果这个函数返回true,那么传入的address一定是PostalAddress类型。
3. 使用switch语句做分支覆盖(最严谨)
switch语句是TypeScript处理联合类型的绝佳场景,它能自动完成类型窄化,还能确保所有分支都被覆盖:
const DeliveryAddressDisplay = (props: Props) => { const address = props.deliveryAddress; return ( <div className="App"> {(() => { switch(address.type) { case AddressType.postal: return ( <> <h2>Street: {address.street}</h2> <h2>Street Number: {address.streetNumber}</h2> </> ); case AddressType.packstation: return ( <> <h2>Packstation: {address.packstation}</h2> <h2>Post Number: {address.postNumber}</h2> </> ); default: // 这里可以抛出错误或者渲染默认内容,确保没有遗漏的类型分支 throw new Error(`Unsupported address type: ${address.type}`); } })()} </div> ); };
这种方式的好处是,如果后续你扩展了AddressType,TypeScript会立刻提示你在switch中添加对应的处理分支,避免遗漏。
内容的提问来源于stack exchange,提问作者Hamza Boularbah

