React组件中API响应联合类型条件渲染的TypeScript报错问题
这是TypeScript类型窄化(Type Narrowing)机制的一个常见问题——你虽然提前判断了地址类型并把结果存在了布尔变量里,但TypeScript没办法追踪这些变量和原对象类型之间的关联,所以在条件分支里,它依然把props.deliveryAddress当成DeliveryAddress联合类型处理,自然会报错说某些属性在另一种类型里不存在。
为什么当前写法不行?
当你把props.deliveryAddress.type === AddressType.postal的结果赋值给isPostalAddress变量后,TypeScript丢失了这个变量和原对象类型的绑定关系。它只知道isPostalAddress是个布尔值,但没办法确定:当isPostalAddress为true时,props.deliveryAddress一定是PostalAddress类型。所以在{isPostalAddress && ...}的分支里,TypeScript依然会认为props.deliveryAddress可能是PackstationAddress,从而报错。
解决方案
这里有几种可靠的解决方式,你可以根据自己的习惯选择:
1. 直接在条件判断中使用类型检查(最直接)
把类型判断逻辑直接写在JSX的条件里,这样TypeScript能直接识别到类型窄化:
const DeliveryAddressDisplay = (props: Props) => { 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; return ( <div className="App"> {isPostalAddress(address) && ( <> <h2>Street: {address.street}</h2> <h2>Street Number: {address.streetNumber}</h2> </> )} {isPackstationAddress(address) && ( <> <h2>Packstation: {address.packstation}</h2> <h2>Post Number: {address.postNumber}</h2> </> )} </div> ); };
3. 使用switch语句(更适合复杂场景)
switch语句的case分支能让TypeScript自动做类型窄化,代码结构也更清晰:
const renderAddress = (address: DeliveryAddress) => { 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> </> ); } }; const DeliveryAddressDisplay = (props: Props) => { return ( <div className="App"> {renderAddress(props.deliveryAddress)} </div> ); };
这些方法都能让TypeScript正确识别条件分支里的具体类型,解决报错问题。
内容的提问来源于stack exchange,提问作者Hamza Boularbah

