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

如何为Story添加额外控件并兼顾TypeScript与Storybook类型安全?

解决Storybook中自定义非组件属性的类型错误问题

我有一个Chip组件,其leftSection属性接受ReactNode类型的额外元素,其中一种用法是渲染ColorSwatch。

在Storybook元数据中,我定义了仅用于Storybook编辑的swatchColor属性,但该属性并非Chip组件本身的属性,导致编写Story对象时触发类型错误:

现有代码及错误情况

Story元数据代码

const meta = {
  component: Chip,
  argTypes: {
    label: { control: 'text' },
    leftSection: {
      control: 'select',
      options: ['none', 'swatch'],
    },
    swatchColor: {
      control: 'color',
      if: { arg: 'leftSection', eq: 'swatch' },
    },
  },
} as Meta<Component>;

Story对象代码(触发类型错误)

export const Default: StoryObj<Component> = {
  render: (args) => {
    const { leftSection, swatchColor, ...rest } = args;
    //                   ^ 类型错误:Property 'swatchColor' does not exist

    if (leftSection === 'swatch') {
      return (
        <Chip
          leftSection={<ColorSwatch color={swatchColor} />}
          {...rest}
        />
      );
    }

    return <Chip {...rest} />;
  },

  args: {
    label: 'foo',
    leftSection: 'none',
  },
};

可行解决方案

方案1:扩展组件Props类型(推荐)

创建一个包含组件原生Props和Storybook自定义属性的联合类型,既保留类型安全,又明确Story可用参数:

import { Meta, StoryObj } from '@storybook/react';
import Chip, { ChipProps } from './Chip';

// 定义Story专属的参数类型,合并组件原生Props和自定义属性
type ChipStoryArgs = ChipProps & {
  leftSection: 'none' | 'swatch';
  swatchColor?: string;
};

const meta = {
  component: Chip,
  argTypes: {
    label: { control: 'text' },
    leftSection: {
      control: 'select',
      options: ['none', 'swatch'],
    },
    swatchColor: {
      control: 'color',
      if: { arg: 'leftSection', eq: 'swatch' },
    },
  },
} as Meta<ChipStoryArgs>;

export default meta;
// 使用自定义的Story参数类型
type Story = StoryObj<ChipStoryArgs>;

export const Default: Story = {
  render: (args) => {
    const { leftSection, swatchColor, ...rest } = args;

    if (leftSection === 'swatch') {
      return (
        <Chip
          leftSection={<ColorSwatch color={swatchColor} />}
          {...rest}
        />
      );
    }

    return <Chip {...rest} />;
  },

  args: {
    label: 'foo',
    leftSection: 'none',
  },
};

方案2:临时类型断言

如果不想修改全局类型,可以在解构时对args进行类型断言,快速解决错误:

export const Default: StoryObj<Component> = {
  render: (args) => {
    // 断言args包含自定义的swatchColor和leftSection枚举值
    const { leftSection, swatchColor, ...rest } = args as Component & { 
      swatchColor?: string; 
      leftSection: 'none' | 'swatch' 
    };

    if (leftSection === 'swatch') {
      return (
        <Chip
          leftSection={<ColorSwatch color={swatchColor} />}
          {...rest}
        />
      );
    }

    return <Chip {...rest} />;
  },

  args: {
    label: 'foo',
    leftSection: 'none',
  },
};

方案3:模块扩充扩展Storybook Args类型

通过TypeScript模块扩充,全局扩展Storybook的Args类型,适用于多个Story需要相同自定义属性的场景:

import { Args } from '@storybook/react';

// 扩充Storybook的Args类型,添加自定义属性
declare module '@storybook/react' {
  interface Args {
    swatchColor?: string;
    leftSection?: 'none' | 'swatch';
  }
}

内容的提问来源于stack exchange,提问作者macabeus

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 19:27:00