土曜日, 5月 10, 2025
No menu items!
ホームニューステックニュースStorybook の parameters の型をいい感じにして便利に使う

Storybook の parameters の型をいい感じにして便利に使う



Storybook の parameters

Storybook の parameters は、Story や Component に静的なメタデータやパラメタを追加するための機能です。これらのパラメータは addon の設定や Story の表示方法のカスタマイズなどに利用されます。


export const parameters = {
  backgrounds: {
    default: 'light',
    values: [
      { name: 'light', value: '#f8f8f8' },
      { name: 'dark', value: '#333333' },
    ],
  },
};


export default {
  component: Button,
  parameters: {
    backgrounds: {
      default: 'dark',
    },
  },
};


export const Primary = {
  args: {  },
  parameters: {
    backgrounds: {
      default: 'light',
    },
  },
};

parameters は多くの addon やツールで利用されており、例えば Storybook で MSW を利用するための msw-storybok-addon や、 VRT(Visual Regression Test) などに利用できるスクリーンショットを生成する storycap あるいは storycap-testrun などの設定は parameters で記述します。

parameters の型安全性の問題

Storybook の parameters は TypeScript で使う際に大きな問題があります。デフォルトでは parameters の型は { [key: string]: any } となってしまっています。

例えば、以下のようなコードを書いたとしても、TypeScript はエラーを検出できません:


export const Primary = {
  parameters: {
    backgrouds: { 
      default: 'light',
    },
  },
};


export const decorator = (Story, context) => {
  const nonExistentValue = context.parameters.nonExistent.value;
  return Story />;
};

これでは TypeScript による補完やエラー検知といった恩恵を受けることができません。特に大規模なプロジェクトや複数人あるいは AI を交えた開発では、型安全性の欠如で問題の検知が遅れると非常に面倒です。

TypeScript の module augmentation と declaration merging

この問題を解決するために、TypeScript の module augmentation と declaration merging という機能を利用できます

Declaration Merging

Declaration merging は、同じ名前の型定義が複数ある場合に、TypeScript がそれらをマージする機能です。特に interface は同じ名前で複数回定義することができ、それらの定義がマージされます。

interface Box {
  height: number;
  width: number;
}
interface Box {
  scale: number;
}
let box: Box = { height: 5, width: 6, scale: 10 };

TypeScript Documentation – Declaration Merging より

Module Augmentation

Module augmentation は、既存のモジュールの型定義を拡張する機能です。declare module "モジュール名" の形式で利用できます。


export class ObservableT> {
  
}


import { Observable } from "./observable";
declare module "./observable" {
  interface ObservableT> {
    mapU>(f: (x: T) => U): ObservableU>;
  }
}

TypeScript Documentation – Module Augmentation より

Storybook の parameters の型を拡張する

Storybook の parameters の型は Parameters という interface で定義されています。この interface を前述の module augmentation と declaration merging を使って拡張することで、型安全に parameters を使うことができます。

基本的な型拡張の方法は以下の通りです:

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

declare module '@storybook/react' {
  interface Parameters {
    myCustomParameter?: {
      value: string;
      enabled: boolean;
    };
  }
}

これにより、parameters.myCustomParameter に型が付き、補完も効くようになります:

export const Primary = {
  parameters: {
    myCustomParameter: {
      value: 'hello', 
      enabled: true,  
    },
  },
};

実際の使用例

外部パッケージの parameters の型定義追加

多くの Storybook addon は parameters を使って設定を受け取りますが、これらのパッケージは独自の型定義を提供していることが多いです。これらの型定義を Storybook の Parameters に統合することで、型安全に使うことができます。

MSW の型定義追加

MSW を Storybook で使う場合、msw-storybook-addon を利用します。このアドオンは MswParameters という型を export しており、これを使って Parameters を拡張できます。

import type { Parameters } from '@storybook/react';
import type { MswParameters } from 'msw-storybook-addon';

declare module '@storybook/react' {
  interface Parameters extends MswParameters {}
}

これにより、MSW のハンドラーを型安全に設定できます:

import { rest } from 'msw';

export const WithMock = {
  parameters: {
    msw: {
      handlers: [
        rest.get('/api/users', (req, res, ctx) => {
          return res(
            ctx.json([
              { id: 1, name: '田中太郎' },
              { id: 2, name: '鈴木花子' },
            ])
          );
        }),
      ],
    },
  },
};

Storycap の型定義追加

Storycap はスクリーンショットを自動生成するアドオンで、ScreenshotParameters という型を提供しています。これを使って Parameters を拡張できます:

import type { Parameters } from '@storybook/react';
import type { ScreenshotParameters } from "storycap-testrun";

declare module '@storybook/react' {
  interface Parameters {
    screenshot?: ScreenshotParameters;
  }
}

これにより、Storycap の設定を型安全に行えます:

export const WithScreenshot = {
  parameters: {
    screenshot: {
      delay: 1000,
    },
  },
};

自前の decorator から parameters を参照する

Storybook では decorator を使って共通の設定を適用することがよくあります。例えば、React Context の Provider など、広い範囲から参照される状態を設定する場合などです。

このように、各 Story ごとに異なる初期値を設定したい場合にも parameters を使うと便利です。

例えば、jotai や React Context などのグローバル状態管理を使っている場合、Story ごとに異なる初期状態を設定したいことがあります:

まず、parameters の型を拡張します:

import type { Parameters } from '@storybook/react';

import { CurrentUser } from '../src/types';

declare module '@storybook/react' {
  interface Parameters {
    initialState?: {
      currentUser?: CurrentUser;
    };
  }
}

次に、この parameters を使用する decorator を作成します:

import { Decorator } from '@storybook/react';
import { UserProvider } from '../src/contexts/UserContext';

export const decorators: Decorator[] = [
  (Story, ctx) => {
    const currentUser = ctx.parameters.initialState?.currentUser ?? null;

    return (
      UserProvider initialUser={currentUser}>
        Story />
      /UserProvider>
    );
  },
];

これにより、各 Story で異なる初期状態を型安全に設定できます:

export const LoggedInUser = {
  parameters: {
    initialState: {
      currentUser: {
        id: 1,
        name: 'Test User',
        email: 'testusr@example.com',
      },
    },
  },
};

このようにコンポーネントの外側で注入される設定や状態を Decorator + Parameters で渡すことで、型安全に・ Story の args と全体の設定・状態を分離しつつ記述できます。

まとめ

Storybook の parameters はデフォルトでは型安全性に欠けますが、TypeScript の module augmentation と declaration merging を活用することで、型安全に使うことができます。

また、parameters は外部パッケージが提供する addon だけでなく、プロジェクト独自の Context Provider への値の注入など、自作の Decorator でも利用することができます。

現代の TypeScript では型があたってないことは基本的に許されないぞ!という気持ちで日々やっていきましょう。


筆者 @izumin5210 が所属する バクラク事業部 PlatformEngineering部 では様々な技術領域について発信をしています。 興味がある方はカジュアル面談などもぜひ!

https://zenn.dev/layerx/articles/7e9f87fca65e94

https://tech.layerx.co.jp/entry/2025/04/24/172412

https://tech.layerx.co.jp/entry/2025/03/31/150000

https://zenn.dev/layerx/articles/9bdefe4d435882

https://jobs.layerx.co.jp/1cdcdd370bae8002a196e013180c7a4b

https://jobs.layerx.co.jp/dfa6f5721f0741e2bf9531beb0fa1b70

フラッグシティパートナーズ海外不動産投資セミナー 【DMM FX】入金

Source link

Views: 2

RELATED ARTICLES

返事を書く

あなたのコメントを入力してください。
ここにあなたの名前を入力してください

- Advertisment -

Most Popular

Recent Comments