ReactとTypeScript:FC・ジェネリクス・フック
ジェネリックなpropsでReactコンポーネントに型を付け、useStateとuseRefに明示的な型を付け、タプルを返すカスタムフックにも型を付けます。
「ReactとTypeScript:FC・ジェネリクス・フック」はCoddyKit上の無料Frontend Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはFrontend Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Frontend Academyコースには全4レッスンが含まれています。
コンポーネントの型付け
現代のReactでは、interfaceでpropsの型を定義し、通常の関数を使用します。React.FCも存在しますが、暗黙的なchildrenやジェネリックの非対応といった理由から、現在は推奨されていません。
// Recommended:
interface ButtonProps {
label: string;
onClick: () => void;
disabled?: boolean;
}
function Button({ label, onClick, disabled }: ButtonProps) {
return <button onClick={onClick} disabled={disabled}>{label}</button>;
}childrenの型付け
描画可能な子要素にはReact.ReactNodeを使用します。
interface CardProps {
title: string;
children: React.ReactNode;
}
function Card({ title, children }: CardProps) {
return (
<div className="card">
<h2>{title}</h2>
{children}
</div>
);
}useStateの型付け
useStateは通常、初期値から型を推論します。初期値がnullの場合や、取り得る型全体を表していない場合は、明示的に型を指定します。
const [count, setCount] = useState(0); // number
const [name, setName] = useState(''); // string
const [user, setUser] = useState<User | null>(null); // explicit
const [items, setItems] = useState<Item[]>([]); // explicit array typeuseRefの型付け
形式は2つあります。DOM要素へのref(nullで初期化)と、可変コンテナーです。
const inputRef = useRef<HTMLInputElement>(null);
const timerRef = useRef<number | null>(null);
useEffect(() => {
inputRef.current?.focus();
timerRef.current = window.setInterval(tick, 1000);
return () => clearInterval(timerRef.current!);
}, []);イベントハンドラーの型付け
Reactの合成イベント型を使用します。
function Form() {
function onSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
}
function onChange(e: React.ChangeEvent<HTMLInputElement>) {
setValue(e.target.value);
}
function onClick(e: React.MouseEvent<HTMLButtonElement>) {
/* ... */
}
return <form onSubmit={onSubmit}>...</form>;
}ジェネリックコンポーネント
コンポーネントをジェネリックにできます。TypeScriptは呼び出し箇所から型パラメーターを推論します。
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
keyOf: (item: T) => string | number;
}
function List<T>({ items, renderItem, keyOf }: ListProps<T>) {
return <ul>{items.map(i => <li key={keyOf(i)}>{renderItem(i)}</li>)}</ul>;
}
<List<User>
items={users}
renderItem={u => u.name}
keyOf={u => u.id}
/>カスタムフックの型付け
カスタムフックは関数なので、ほかの関数と同じように型付けします。配列型への拡大解釈を避けるため、タプルを返す場合は明示的なアノテーションが必要です。
function useToggle(initial = false): [boolean, () => void] {
const [value, setValue] = useState(initial);
const toggle = () => setValue(v => !v);
return [value, toggle]; // tuple — annotation makes this stable
}
const [isOpen, toggle] = useToggle();Contextの型付け
createContextには、提供する値と同じ型の初期値が必要です。non-null assertionまたはデフォルトオブジェクトを使用します。
interface AuthContextValue {
user: User | null;
signIn: (email: string, pw: string) => Promise<void>;
signOut: () => void;
}
const AuthContext = createContext<AuthContextValue | null>(null);
export function useAuth() {
const ctx = useContext(AuthContext);
if (!ctx) throw new Error('useAuth must be inside AuthProvider');
return ctx;
}useReducerの型付け
状態とアクションのユニオンを明示的に型付けします。
type State = { count: number };
type Action =
| { type: 'increment' }
| { type: 'decrement' }
| { type: 'set'; value: number };
function reducer(state: State, action: Action): State {
switch (action.type) {
case 'increment': return { count: state.count + 1 };
case 'decrement': return { count: state.count - 1 };
case 'set': return { count: action.value };
}
}
const [state, dispatch] = useReducer(reducer, { count: 0 });React.FCを避ける
React.FCは次第に使われなくなっています。childrenを暗黙的に追加し、ジェネリックコンポーネントを適切にサポートせず、一部の最適化を妨げるためです。型付けしたpropsを持つ通常の関数コンポーネントを推奨します。
ComponentPropsWithoutRefユーティリティ
ネイティブ要素のpropsを継承する(buttonやinputを拡張する)には、ComponentPropsWithoutRefを使用します。
type ButtonProps = React.ComponentPropsWithoutRef<'button'> & {
variant?: 'primary' | 'secondary';
};
function Button({ variant = 'primary', ...rest }: ButtonProps) {
return <button className={`btn ${variant}`} {...rest} />;
}クイックチェック
通常の型付き関数コンポーネントを使うほうがReact.FCより一般的に推奨されるのはなぜですか?
復習:TypeScript + React
propsはインターフェースで型付けし、React.FCより通常の関数を優先します。childrenにはReact.ReactNodeを使用します。useStateは型を推論できることが多いですが、nullや空の配列の場合は型注釈を付けます。DOM要素にはuseRefを使用します。イベントハンドラーにはReactのイベント型を使用します。再利用可能なリストやフォームにはジェネリックコンポーネントを使用します。カスタムフックがタプルを返す場合は型注釈が必要です。ネイティブ要素を拡張するにはComponentPropsWithoutRefを使用します。
よくある質問
「ReactとTypeScript:FC・ジェネリクス・フック」レッスンは無料ですか?
はい。「ReactとTypeScript:FC・ジェネリクス・フック」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Frontend Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Frontend Academyコースには全4レッスンが含まれています。
「ReactとTypeScript:FC・ジェネリクス・フック」で何を学びますか?
ジェネリックなpropsでReactコンポーネントに型を付け、useStateとuseRefに明示的な型を付け、タプルを返すカスタムフックにも型を付けます。 ブラウザで直接実行するハンズオンコードでFrontend Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Frontend Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのFrontend Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「ReactとTypeScript:FC・ジェネリクス・フック」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このFrontend Academyレッスンでコードを書いて実行できますか?
はい。すべてのFrontend Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- テンプレートリテラル型
- デコレーターとメタデータ
- ReactとTypeScript:FC・ジェネリクス・フック
- Strictモードとanyの排除