Polymorphic Components パターンで作る、型安全でセマンティックな UI コンポーネント
by
ryo
×
Copy
Open
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Slide 1
Slide 1 text
Polymorphic Components パターン で作る、型安全でセマンティックな UI コンポーネント 2026.5.23 TSKaigi2026 ryo ( @ryohiy_ )
Slide 2
Slide 2 text
株式会社Belong FEE/SWE 最近は Three.js を好きで触っています ryo (@ryohiy_) 自己紹介
Slide 3
Slide 3 text
例えば、以下のような Text コンポーネントを実装 デザインシステム コンポーネント実装時の課題 export function Text({ children, ...props }) { return
{children}
}
Slide 4
Slide 4 text
例えば、以下のような Text コンポーネントを実装 デザインシステム コンポーネント実装時の課題 export function Text({ children, ...props }) { return
{children}
} Text を使うと常に span になる -> ❌ Semantic HTML 呼び出し側で要素を自由に宣言出来るようにしたい
Slide 5
Slide 5 text
デザインシステムのコンポーネント実装やUI ライブラリで利用 するデザインパターン コンポーネントの要素を呼び出し側から宣言する事を 出来るようにする Polymorphic Components とは
Slide 6
Slide 6 text
実装例 export function Text({ as, children, ...props }) { const Component = as || 'span' return {children} } Text コンポーネントを Polymorphic にすると
Slide 7
Slide 7 text
実装例 export function Text({ as, children, ...props }) { const Component = as || 'span' return {children} } Text コンポーネントを Polymorphic にすると as として HTML Tag の指定を受け取り、jsx の type として利用する ※ 型 assertion の 「as」とは異なるものです。これは Polymorphic Components の as prop パターン
Slide 8
Slide 8 text
実装例 ページタイトル 2026年5月23日 利用側:
ページタイトル
2026年5月23日
レンダリング後:
Slide 9
Slide 9 text
実装例 利用側: レンダリング後: ❌ as= hoge ❌ as=h1 , dateTime=”2026-05-23” ページタイトル 2026年5月23日
ページタイトル
2026年5月23日
Slide 10
Slide 10 text
実装例 利用側: レンダリング後: ❌ as= hoge ❌ as=h1 , dateTime=”2026-05-23” ページタイトル 2026年5月23日 コードを書いている時に気づけるようにしたい => 型定義を活用する
ページタイトル
2026年5月23日
Slide 11
Slide 11 text
実装① cva でデザイントークンを定義 const textVariants = cva('', { variants: { size: { md: 'text-base', lg: 'text-3xl', }, weight: { normal: 'font-normal', bold: 'font-bold', }, } })
Slide 12
Slide 12 text
実装② 自前の props を定義 interface TextOwnProps extends VariantProps { as?: T }
Slide 13
Slide 13 text
実装② 自前の props を定義 interface TextOwnProps extends VariantProps { as?: T }
Slide 14
Slide 14 text
実装③ HTML 要素の props と合成 type TextProps = TextOwnProps & Omit, keyof TextOwnProps | 'className'> の HTML 属性を取得
Slide 15
Slide 15 text
実装③ HTML 要素の props と合成 type TextProps = TextOwnProps & Omit, keyof TextOwnProps | 'className'> 自前 prop と衝突するキーを除外
Slide 16
Slide 16 text
実装④ コンポーネントを実装 export function Text({ as, size, weight, children, ...props }: TextProps) { const Component = as || 'span' return ( {children} )}
Slide 17
Slide 17 text
実装④ コンポーネントを実装 export function Text({ as, size, weight, children, ...props }: TextProps) { const Component = as || 'span' return ( {children} )} ②と同じ形
Slide 18
Slide 18 text
実装④ コンポーネントを実装 export function Text({ as, size, weight, children, ...props }: TextProps) { const Component = as || 'span' return ( {children} )} ③の実装
Slide 19
Slide 19 text
利用例 ページタイトル // =>
ページタイトル
本文テキスト // =>
本文テキスト
2026年5月23日 // =>
2026年5月23日
OK
Slide 20
Slide 20 text
利用例 ページタイトル // =>
ページタイトル
本文テキスト // =>
本文テキスト
2026年5月23日 // =>
2026年5月23日
OK
Slide 21
Slide 21 text
利用例 ページタイトル // =>
ページタイトル
本文テキスト // =>
本文テキスト
2026年5月23日 // =>
2026年5月23日
OK
Slide 22
Slide 22 text
利用例 ページタイトル // =>
ページタイトル
本文テキスト // =>
本文テキスト
2026年5月23日 // =>
2026年5月23日
OK
Slide 23
Slide 23 text
利用例 ページタイトル // => ElementType で type check error 本文テキスト // => size は sm を持たないため type check error 2026年5月23日 // => h1 は dateTime 属性を持たないため type check error NG
Slide 24
Slide 24 text
利用例 ページタイトル // => ElementType で type check error 本文テキスト // => size は sm を持たないため type check error 2026年5月23日 // => h1 は dateTime 属性を持たないため type check error NG
Slide 25
Slide 25 text
利用例 ページタイトル // => ElementType で type check error 本文テキスト // => size は sm を持たないため type check error 2026年5月23日 // => h1 は dateTime 属性を持たないため type check error NG
Slide 26
Slide 26 text
利用例 ページタイトル // => ElementType で type check error 本文テキスト // => size は sm を持たない為 type check error 2026年5月23日 // => h1 は dateTime 属性を持たないため type check error NG
Slide 27
Slide 27 text
参考資料 ● https://github.com/shadcn-ui/ui ● https://www.components.build/polymorphism ● https://web.dev/learn/html/semantic-html ● https://developer.mozilla.org/ja/docs/Glossary/Semantics ● https://developer.mozilla.org/ja/docs/Learn_web_development/Core/ Accessibility/HTML ● https://zenn.dev/tsuboi/articles/8abddb1ae3038f
Slide 28
Slide 28 text
おわりに 本題のブログ Belong 採用