如何为TypeScript联合类型创建并正确使用类型守卫?
问题背景
我有以下TypeScript类型定义:
export type TeaserOne = { __typename: 'TeaserOne' } & TeaserOneItem; export type TeaserTwo = { __typename: 'TeaserTwo' } & TeaserTwoItem; export type TeaserThree = { __typename: 'TeaserThree' } & TeaserThreeItem; export type Teaser = TeaserOne | TeaserTwo | TeaserThree; export type FilteredTeasers = Exclude<Teaser, TeaserThree>; export type TeaserItem = TeaserOneItem | TeaserTwoItem | TeaserThreeItem; // 子项类型示例: // TeaserOneItem: export interface TeaserOneItem { type: string; id: string; title: string; lead?: string; } // TeaserTwoItem: export interface TeaserTwoItem { type: string; id: string; title: string; duration?: string; image?: Image; }
在React组件中,我需要用TypeScript类型守卫优化现有代码:
const filteredItems = data.items?.filter( (item) => item?.__typename.includes('TeaserOne') || item?.__typename.includes('TeaserTwo') ); // --> 我想用类型守卫重构这段过滤逻辑,该怎么做? {filteredItems.map((node, index) => { if (isTeaserOne() // <-- 想在这里用类型守卫) { const teaserOne = node as TeaserOneType; return ( <TeaserOne key={teaserOne.id} title={teaserOne.title} lead={teaserOne.lead} /> ); } const teaserTwo = node as TeaserTwoType; return ( <TeaserTwo key={teaserTwo.id} title={teaserTwo.title} image={teaserTwo.image} /> ); })}
我尝试写了一个类型守卫函数,但不确定怎么正确使用,或者是否需要重构:
const isTeaserOne = (teaser: TeaserItem): teaser is TeaserOneItem => typeof teaser === 'object' && teaser.type.includes('TeaserOne');
解决方案
1. 调整类型守卫适配Teaser联合类型
你当前的守卫只针对TeaserItem,但实际处理的是包含__typename的Teaser类型,需要调整参数类型和判断逻辑:
// 针对Teaser类型的TeaserOne守卫 const isTeaserOne = (teaser: Teaser): teaser is TeaserOne => teaser.__typename === 'TeaserOne'; // 同理实现TeaserTwo的类型守卫 const isTeaserTwo = (teaser: Teaser): teaser is TeaserTwo => teaser.__typename === 'TeaserTwo'; // 过滤用的类型守卫,判断是否属于FilteredTeasers类型 const isFilteredTeaser = (teaser: Teaser): teaser is FilteredTeasers => isTeaserOne(teaser) || isTeaserTwo(teaser);
2. 用类型守卫重构过滤逻辑
原过滤逻辑无法让TypeScript自动推导类型,改用isFilteredTeaser作为filter回调后,TypeScript会自动将filteredItems的类型推断为FilteredTeasers[]:
const filteredItems = data.items?.filter(isFilteredTeaser);
3. 在map中使用类型守卫
直接将node传入isTeaserOne即可,TypeScript会自动缩小类型范围,无需额外断言:
{filteredItems?.map((node) => { if (isTeaserOne(node)) { return ( <TeaserOne key={node.id} title={node.title} lead={node.lead} /> ); } // 此处TypeScript会自动推断node为TeaserTwo类型 return ( <TeaserTwo key={node.id} title={node.title} image={node.image} /> ); })}
补充:针对TeaserItem类型的守卫用法
如果确实需要处理TeaserItem类型,你的原守卫可以保留,使用方式如下:
// 假设处理的是TeaserItem数组 const filteredItems = itemList.filter(item => item.type.includes('TeaserOne') || item.type.includes('TeaserTwo')); filteredItems.map(item => { if (isTeaserOne(item)) { // item自动推断为TeaserOneItem return <TeaserOne key={item.id} title={item.title} lead={item.lead} />; } // 自动推断为TeaserTwoItem return <TeaserTwo key={item.id} title={item.title} image={item.image} />; });
内容的提问来源于stack exchange,提问作者meez
相关产品推荐
相关产品推荐

