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

使用TypeScript+Storybook开发React库时stories文件编译报错求助

React组件库开发:Storybook故事文件编译报错

问题背景

使用TypeScript+Storybook开发React组件库,组件编译完成后导入使用正常,但编译过程中*.stories.tsx文件出现两处类型错误。

相关文件代码

Button.tsx

import React, {forwardRef, useEffect, useImperativeHandle, useState} from 'react'
import ButtonPropTypes from './ButtonPropTypes'

const Button = forwardRef((props: InferProps<typeof ButtonPropTypes>, buttonRef) => {...}

ButtonPropTypes.ts

import PropTypes from 'prop-types'

export default {
  to: PropTypes.string,
  label: PropTypes.any,
  children: PropTypes.any,
  disabled: PropTypes.bool,
  loading: PropTypes.bool,
  onClick: PropTypes.func,
  state: PropTypes.object,
  type: PropTypes.string.isRequired,
  use: PropTypes.string.isRequired
}

(为简化内容,省略了部分propTypes)

Button.stories.tsx

import React from 'react'
import {Story, ComponentMeta} from '@storybook/react'
import Button from './Button'
import ButtonPropTypes from './ButtonPropTypes'
import {InferProps} from 'prop-types'

type ButtonPropsInterface = InferProps<typeof ButtonPropTypes>

export default {
  title: 'RecylinkReactComponents/Button',
  component: Button
} as ComponentMeta<typeof Button>

const Template: Story<ButtonPropsInterface> = args => <Button {...args} /> 
// 错误1:类型'(args: any) => Element'与类型'Story<ButtonPropsInterface>'无共同属性

export const TestButton = Template.bind({})
// 错误2:类型'Story<ButtonPropsInterface>'上不存在属性'bind'
TestButton.args = {
  label: 'Label',
  onClick: () => console.log('Clicked'),
  type: 'button',
  use: 'function'
}

rollup.config.js

import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import typescript from '@rollup/plugin-typescript'
import postcss from 'rollup-plugin-postcss'
import dts from 'rollup-plugin-dts'

import {terser} from 'rollup-plugin-terser'
import peerDepsExternal from 'rollup-plugin-peer-deps-external'

const packageJson = require('./package.json')

export default [
  {
    input: 'src/index.ts',
    output: [
      {
        file: packageJson.main,
        format: 'cjs',
        sourcemap: true
      },
      {
        file: packageJson.module,
        format: 'esm',
        sourcemap: true
      }
    ],
    plugins: [
      peerDepsExternal(),
      resolve(),
      commonjs(),
      typescript({
        tsconfig: './tsconfig.json',
        exclude: [
          /\.test.((js|jsx|ts|tsx))$/,
          /\.stories.((js|jsx|ts|tsx|mdx))$/
        ]
      }),
      postcss(),
      terser()
    ],
    context: 'window'
  },
  {
    input: 'dist/esm/index.d.ts',
    output: [{file: 'dist/index.d.ts', format: 'esm'}],
    plugins: [dts()],
    external: [/\.css$/],
    context: 'window'
  }
]

tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true,

    "jsx": "react",
    "module": "ESNext",
    "declaration": true,
    "declarationDir": "types",
    "sourceMap": true,
    "outDir": "dist",
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "emitDeclarationOnly": true,
    "allowJs": true,
    "noImplicitAny": false
  }
}

.storybook/main.js

module.exports = {
  "stories": [
    "../src/**/*.stories.mdx",
    "../src/**/*.stories.@(js|jsx|ts|tsx)"
  ],
  "addons": [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "@storybook/addon-interactions"
  ],
  "framework": "@storybook/react",
  "core": {
    "builder": "@storybook/builder-webpack5"
  }
}

错误信息

  1. 模板函数赋值行:类型'(args: any) => Element'与类型'Story<ButtonPropsInterface>'无共同属性
  2. Template.bind({})行:类型'Story<ButtonPropsInterface>'上不存在属性bind

解决方案

原因

Story是Storybook提供的包含属性和方法的对象类型,并非单纯的函数类型;而bind是JS函数的原生方法,Story对象并不具备该方法,因此直接赋值函数会触发类型不匹配错误。

修复方案

方案1:替换为StoryFn类型

StoryFn是Storybook专为故事模板函数设计的类型,直接替换即可匹配:

import React from 'react'
// 替换Story为StoryFn
import {StoryFn, ComponentMeta} from '@storybook/react'
import Button from './Button'
import ButtonPropTypes from './ButtonPropTypes'
import {InferProps} from 'prop-types'

type ButtonPropsInterface = InferProps<typeof ButtonPropTypes>

export default {
  title: 'RecylinkReactComponents/Button',
  component: Button
} as ComponentMeta<typeof Button>

// 用StoryFn定义模板类型
const Template: StoryFn<ButtonPropsInterface> = args => <Button {...args} /> 

export const TestButton = Template.bind({})
TestButton.args = {
  label: 'Label',
  onClick: () => console.log('Clicked'),
  type: 'button',
  use: 'function'
}

方案2:直接从组件导出Props类型(更推荐)

先修改Button.tsx,导出Props类型:

import React, {forwardRef, useEffect, useImperativeHandle, useState} from 'react'
import ButtonPropTypes from './ButtonPropTypes'
import {InferProps} from 'prop-types'

// 导出组件Props类型
export type ButtonProps = InferProps<typeof ButtonPropTypes>

const Button = forwardRef((props: ButtonProps, buttonRef) => {...}

export default Button

再修改stories文件,直接导入使用:

import React from 'react'
import {StoryFn, ComponentMeta} from '@storybook/react'
// 导入组件及Props类型
import Button, {ButtonProps} from './Button'

export default {
  title: 'RecylinkReactComponents/Button',
  component: Button
} as ComponentMeta<typeof Button>

const Template: StoryFn<ButtonProps> = args => <Button {...args} /> 

export const TestButton = Template.bind({})
TestButton.args = {
  label: 'Label',
  onClick: () => console.log('Clicked'),
  type: 'button',
  use: 'function'
}

额外优化建议

  • 避免同时使用prop-types和TypeScript,直接用TS接口定义组件Props,减少冗余且类型检查更严格
  • 确保Storybook类型依赖完整,执行npm install @types/storybook-react --save-dev或yarn add @types/storybook-react --dev

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 02:10:35