如何为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
相关产品推荐
相关产品推荐

