0Pricing
React Academy · レッスン

Generic ComponentsとUtility Types

再利用可能なgeneric componentsを作成し、PropsWithChildren、ComponentProps、Omitを使います。

「Generic ComponentsとUtility Types」はCoddyKit上の無料React Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはReact Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 React Academyコースには全4レッスンが含まれています。

ジェネリックコンポーネントを使う理由

ジェネリックコンポーネントは、完全な型安全性を保ちながらさまざまなデータ型に対応します。つまり、どのようなデータ構造でも使える、型付きのリスト、セレクト、テーブルを作成できます。

シンプルなジェネリックリスト

関数に型パラメーターTを指定すると、任意の項目型を描画できるリストコンポーネントを作成できます。

interface ListProps<T> {
  items: T[];
  renderItem: (item: T, index: number) => React.ReactNode;
  keyExtractor: (item: T) => string;
}

function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
  return (
    <ul>
      {items.map((item, i) => (
        <li key={keyExtractor(item)}>{renderItem(item, i)}</li>
      ))}
    </ul>
  );
}

ジェネリック型の制約

extendsを使用して制約を追加すると、その型が持つ必要のあるプロパティをTypeScriptに認識させられます。

interface SelectProps<T extends { id: string; label: string }> {
  options: T[];
  value: T | null;
  onChange: (option: T) => void;
}

function Select<T extends { id: string; label: string }>(
  { options, value, onChange }: SelectProps<T>
) {
  return (
    <select value={value?.id} onChange={e => onChange(options.find(o => o.id === e.target.value)!)}>
      {options.map(o => <option key={o.id} value={o.id}>{o.label}</option>)}
    </select>
  );
}

PropsWithChildren

React.PropsWithChildren<P>は、任意のProps型にchildren?: React.ReactNodeを追加します。ラッパーコンポーネントに便利です。

function Card({ title, children }: React.PropsWithChildren<{ title: string }>) {
  return (
    <div className="card">
      <h2>{title}</h2>
      {children}
    </div>
  );
}

ComponentProps

React.ComponentProps<typeof Component>は既存のコンポーネントのPropsを抽出します。コンポーネントを拡張またはラップしたい場合に便利です。

import { Button } from './Button';

type ExtendedButtonProps = React.ComponentProps<typeof Button> & {
  loading?: boolean;
};

function LoadingButton({ loading, children, ...props }: ExtendedButtonProps) {
  return (
    <Button {...props} disabled={loading || props.disabled}>
      {loading ? <Spinner /> : children}
    </Button>
  );
}

ComponentPropsWithoutRef

React.ComponentPropsWithoutRef<'input'>は、ref Propsを除いたHTML要素のPropsを抽出します。HTML要素をラップする場合に最適です。

type InputProps = React.ComponentPropsWithoutRef<'input'> & {
  label: string;
  error?: string;
};

function FormInput({ label, error, ...inputProps }: InputProps) {
  return (
    <div>
      <label>{label}</label>
      <input {...inputProps} />
      {error && <span className="error">{error}</span>}
    </div>
  );
}

OmitとPick

Omit<T, Keys>を使用すると特定のPropsを削除でき、Pick<T, Keys>を使用すると既存の型から特定のPropsだけを残せます。

interface UserCardProps {
  id: string;
  name: string;
  email: string;
  avatar: string;
  role: string;
}

// Only expose non-sensitive fields:
type PublicUserCardProps = Omit<UserCardProps, 'email' | 'role'>;
// Or pick exactly what you need:
type AvatarProps = Pick<UserCardProps, 'name' | 'avatar'>;

PartialとRequired

Partial<T>はすべてのPropsをオプションにします(更新フォームに便利です)。Required<T>はすべてのPropsを必須にします。

interface UserFormData { name: string; email: string; bio: string; }

// For update: only send changed fields
type UserUpdatePayload = Partial<UserFormData>;

// For create: all fields are mandatory
type UserCreatePayload = Required<UserFormData>;

型付き辞書に使うRecord

Record<Keys, Value>は、特定のキー型を持つオブジェクト型を作成します。検索マップや翻訳に便利です。

type Status = 'idle' | 'loading' | 'success' | 'error';

const statusLabels: Record<Status, string> = {
  idle: 'Ready',
  loading: 'Loading...',
  success: 'Done!',
  error: 'Something went wrong',
};

フックの型に使うReturnType

ReturnType<typeof useMyHook>を使用すると、型を手動で重複して定義せずに、カスタムフックの戻り値の型を推論できます。

function useCounter(initial: number) {
  const [count, setCount] = useState(initial);
  return { count, increment: () => setCount(c => c + 1) };
}

type CounterHook = ReturnType<typeof useCounter>;
// { count: number; increment: () => void }

ジェネリックなテーブルコンポーネント

どのような行の形にも対応し、カラムのキーを推論しながら、オートコンプリートも利用できる、完全に型安全なデータテーブルです。

interface Column<T> {
  key: keyof T;
  header: string;
  render?: (value: T[keyof T], row: T) => React.ReactNode;
}

function DataTable<T extends object>({ data, columns }: { data: T[]; columns: Column<T>[] }) {
  return (
    <table>
      <thead><tr>{columns.map(c => <th key={String(c.key)}>{c.header}</th>)}</tr></thead>
      <tbody>{data.map((row, i) => (
        <tr key={i}>{columns.map(c => <td key={String(c.key)}>{c.render ? c.render(row[c.key], row) : String(row[c.key])}</td>)}</tr>
      ))}</tbody>
    </table>
  );
}

クイックチェック

既存のTypeScriptインターフェースから指定したキーを削除するユーティリティ型はどれですか?

まとめ

ジェネリックコンポーネントは型パラメーターを使用して、さまざまなデータ構造に対する型安全性を維持します。Omit、Pick、Partial、ComponentProps、PropsWithChildrenなどのユーティリティ型により、手動での型の重複をなくし、コンポーネントAPIをより組み合わせやすくできます。

よくある質問

「Generic ComponentsとUtility Types」レッスンは無料ですか?

はい。「Generic ComponentsとUtility Types」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、React Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 React Academyコースには全4レッスンが含まれています。

「Generic ComponentsとUtility Types」で何を学びますか?

再利用可能なgeneric componentsを作成し、PropsWithChildren、ComponentProps、Omitを使います。 ブラウザで直接実行するハンズオンコードでReact Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

React Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのReact Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「Generic ComponentsとUtility Types」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このReact Academyレッスンでコードを書いて実行できますか?

はい。すべてのReact Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. PropsとComponent Return Typesの型付け
  2. TypeScriptでのEventsとRefsの型付け
  3. Generic ComponentsとUtility Types
  4. ContextとCustom Hooksの型付け
← React Academyに戻る