TypeScript との連携:型安全なコンポーネント
この章で学ぶこと
Button コンポーネントを支える TypeScript の型を学びます。React 19 では、型の書き方が以前よりシンプルになりました。
なぜ型安全性が重要なのか
型があると、コンポーネントの「使い方」がコードに表現され、間違いを未然に防げます。
型がもたらす 4 つの価値
├─ エディタ支援:自動補完/その場でドキュメント
├─ エラー防止:コンパイル時に検出/タイポを防ぐ
├─ 安全なリファクタ:変更の影響が分かる
└─ 自己文書化:使い方が型で分かる
Button の引数の型
完成形の章の Button は、引数に直接、型を書いていました。
function Button({
className,
variant,
size,
asChild = false,
...props
}: React.ComponentProps<"button"> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean
}) {
// ...
}
型の部分は 3 つを &(交差型)でつないでいます。それぞれを見ていきましょう。
型に名前を付けたいときは、type で別出しにもできます。
type ButtonProps = React.ComponentProps<"button"> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean
}
function Button({ className, variant, size, asChild = false, ...props }: ButtonProps) {
// ...
}
① React.ComponentProps<"button">
React.ComponentProps<"button"> は、HTML の <button> が受け取れるすべての propsを表す型です。
// onClick, disabled, type, aria-*, children… そして ref まで含まれる
<Button type="submit" disabled={isLoading} onClick={handleClick} aria-label="送信">
送信
</Button>
これを使えば、onClick や disabled などを自分で 1 つずつ定義する必要はありません。
// ❌ 手で全部書くのは大変
type ButtonProps = {
onClick?: () => void
disabled?: boolean
type?: "submit" | "reset" | "button"
// …何十個も…
}
// ✅ ComponentProps<"button"> ですべてまとめて取り込む
type ButtonProps = React.ComponentProps<"button">
以前は React.ButtonHTMLAttributes<HTMLButtonElement> という型がよく使われていました。React.ComponentProps<"button"> はそれを含み、さらに ref も含むため、より簡潔です。React 19 では ref が通常の props として渡せるので、この型だけで ref 対応が完結します。最新の shadcn/ui もこちらを使っています。
② VariantProps
VariantProps は、cva の定義から variant や size の型を自動生成します。
import { cva, type VariantProps } from "class-variance-authority"
const buttonVariants = cva("...", {
variants: {
variant: { default: "...", destructive: "...", outline: "..." },
size: { default: "...", sm: "...", lg: "..." },
},
})
type ButtonVariants = VariantProps<typeof buttonVariants>
// => {
// variant?: "default" | "destructive" | "outline" | null | undefined
// size?: "default" | "sm" | "lg" | null | undefined
// }
定義を変えれば、型も自動で追従します。
// cva に ghost / link を足すと…
variant: { default: "...", destructive: "...", outline: "...", ghost: "...", link: "..." }
// VariantProps も自動で更新される
// variant?: "default" | "destructive" | "outline" | "ghost" | "link" | null
「定義と型がズレない」のが、cva + VariantProps の大きな利点です。
③ 追加の prop
最後に、Button 独自の asChild を足します。
{ asChild?: boolean }
この 3 つを & でつなぐと、Button の完全な型ができあがります。
React.ComponentProps<"button"> (button の標準 props + ref)
&
VariantProps<typeof buttonVariants> (cva 由来の variant / size)
&
{ asChild?: boolean } (独自 prop)
=
Button の引数の型
React 19 では forwardRef のジェネリクスが要らない
以前は、ref の型を forwardRef のジェネリクスで指定していました。
// React 18 以前
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => { ... })
// ↑ ref の型 ↑ props の型
React 19 では ref が通常の props として渡されるようになり、その型は React.ComponentProps<"button"> に含まれているので、関数の引数に型を書くだけで済みます。forwardRef のジェネリクスは不要になりました。
// React 19
function Button({ ...props }: React.ComponentProps<"button"> & ...) { ... }
型安全な使用例
自動補完
<Button variant="" />
// "" の中で補完を出すと:
// "default" / "destructive" / "outline" / "secondary" / "ghost" / "link"
タイポ・存在しない値の検出
<Button variant="primry" /> // ❌ "primary" のつもりが誤字 → 型エラー
<Button size="extra-large" /> // ❌ 存在しないサイズ → 型エラー
ref の型チェック
const buttonRef = useRef<HTMLButtonElement>(null)
<Button ref={buttonRef}>OK</Button> // ✅
const inputRef = useRef<HTMLInputElement>(null)
<Button ref={inputRef}>NG</Button> // ❌ input 用の ref は渡せない
少し高度な型
必要に応じて、標準の型ユーティリティも使えます。
// className を外部から渡せないようにする(Omit で除外)
type StrictButtonProps = Omit<React.ComponentProps<"button">, "className"> &
VariantProps<typeof buttonVariants>
// 一部の props を任意にする(Partial)
type PartialButtonProps = Partial<ButtonProps>
初めは使う機会は少ないですが、「こういう調整もできる」と知っておくと役立ちます。
型定義のベストプラクティス
- HTML 属性は
ComponentPropsで取り込む(手書きしない) - バリアントの型は
VariantPropsで自動生成する(定義とズレさせない) - 明確な型名を使う(
PropsではなくButtonProps) - 利用者が使う型は
exportする(export type ButtonProps/export { Button, buttonVariants })
この章のまとめ
- Button の型は
ComponentProps<"button">+VariantProps+{ asChild }の交差型 React.ComponentProps<"button">は button の全 props(ref含む)をまとめて取り込むVariantPropsは cva の定義から型を自動生成し、定義とズレない- React 19 では
forwardRefのジェネリクスが不要になり、引数に型を書くだけでよい
次章では、ここまで学んだすべてを使って、Button をゼロから組み立てます。