You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 23:16:12