在 TypeScript 中编写故事
使用 TypeScript 编写故事可以提高效率。您无需在文件之间来回跳转查找组件属性。您的代码编辑器会提醒您缺少必填属性,甚至会自动完成属性值,就像在应用程序中使用组件时一样。此外,Storybook 会推断这些组件类型,自动生成 Controls(控制面板) 表格。
Storybook 内置了 TypeScript 支持,因此无需任何配置即可开始使用。
使用 Meta
和 StoryObj
为故事添加类型
在编写故事时,有两个方面添加类型会很有帮助。首先是 组件元数据 (component meta),它描述和配置组件及其故事。在 CSF 文件中,这是默认导出。其次是 故事本身。
Storybook 为这两者提供了实用的类型,命名为 Meta
和 StoryObj
。这是一个使用这些类型的 CSF 文件示例:
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { Button } from './Button';
const meta = {
component: Button,
} satisfies Meta<typeof Button>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Basic = {} satisfies Story;
export const Primary = {
args: {
primary: true,
},
} satisfies Story;
Props 类型参数
Meta
和 StoryObj
类型都是 泛型,因此您可以为它们提供可选的属性类型参数,用于组件类型或组件的属性类型(例如,Meta<typeof Button>
中的 typeof Button
部分)。这样做,TypeScript 将阻止您定义无效的 arg,并且所有 装饰器、play 函数 或 loaders 都将为其函数参数添加类型。
上面的示例传递了一个组件类型。有关传递属性类型的示例,请参阅 为自定义 args 添加类型。
使用 satisfies
提高类型安全性
如果您使用的是 TypeScript 4.9+,则可以利用新的 satisfies
运算符来获得更严格的类型检查。现在,您将收到缺少必填 arg 的类型错误,而不仅仅是无效 arg 的错误。
使用 satisfies
应用故事类型有助于在故事之间共享 play 函数 时保持类型安全。如果没有它,TypeScript 将抛出错误,指示 play
函数可能未定义。satisfies
运算符使 TypeScript 能够推断 play 函数是否已定义。
最后,使用 satisfies
允许您将 typeof meta
传递给 StoryObj
泛型。这会告知 TypeScript meta
和 StoryObj
类型之间的关联,从而允许它从 meta
类型推断出 args
类型。换句话说,TypeScript 将理解 args 可以在故事和 meta 级别定义,并且当必填 arg 在 meta 级别定义但在故事级别未定义时,不会抛出错误。
为自定义 args 添加类型
有时故事需要定义不包含在组件属性中的 args。在这种情况下,您可以使用交叉类型来组合组件的属性类型和自定义 args 的类型。例如,您可以使用 footer
arg 来填充子组件,如下所示:
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { Page } from './Page';
type PagePropsAndCustomArgs = React.ComponentProps<typeof Page> & { footer?: string };
const meta = {
component: Page,
render: ({ footer, ...args }) => (
<Page {...args}>
<footer>{footer}</footer>
</Page>
),
} satisfies Meta<PagePropsAndCustomArgs>;
export default meta;
type Story = StoryObj<typeof meta>;
export const CustomFooter = {
args: {
footer: 'Built with Storybook',
},
} satisfies Story;