メインコンテンツまでスキップ

第33章:useState と「型推論」

この章では、TypeScript が useState の型を「いい感じに決めてくれる仕組み」=型推論にフォーカスします ✨


1️⃣ この章のゴール 🎯

この章が終わるころには…

  • useState(0) と書くだけで、なぜ countnumber 型になるのか説明できる
  • useState('')useState(false) など、シンプルな初期値は型推論に任せてOKなパターンがわかる
  • 「このパターンは型推論に任せていい」「ここはちゃんと型を書くべき」の直感が少し育つ

2️⃣ さくっと復習:useState って何だっけ? 💡

useState は、「値」と「その値を変える関数」をセットでくれるフックでしたね。(React)

例えばカウンターなら:

import { useState } from 'react';

function Counter() {
const [count, setCount] = useState(0);

const handleClick = () => {
setCount(count + 1);
};

return (
<button onClick={handleClick}>
いまのカウント: {count}
</button>
);
}

export default Counter;

ここで大事なのはこの1行 👇

const [count, setCount] = useState(0);

実はこれ、TypeScript 的にはだいたいこう解釈されています(ざっくりイメージ):

  • count の型 → number
  • setCount の型 → (next: number) => void

…え、そんなことどこにも書いてないのに!?というところで登場するのが 型推論 です 🧚‍♀️


3️⃣ 型推論ってなに?🧠✨

まずはシンプルな TypeScript の例から:

let age = 20;
let message = 'こんにちは';
let isStudent = true;

このとき、TypeScript は自動で:

  • agenumber
  • messagestring
  • isStudentboolean

と**勝手に型を決めてくれます。これが「型推論」**です。(typescriptlang.org)

だから、わざわざ

let age: number = 20;

と書かなくても OK な場面が多いんでしたね 👍


4️⃣ useState × 型推論:初期値から全部決まる 💫

useState も同じノリで、渡した「最初の値」をヒントに型を推論します。(Medium)

4-1. 数値の例:useState(0)

const [count, setCount] = useState(0);
  • 初期値が 0(number)なので → countnumber 型 → setCount は「number を受け取る関数」になります

VS Code で count にマウスを乗せると、たぶんこんな感じの型が見えるはずです(イメージ):

const count: number;
const setCount: React.Dispatch<React.SetStateAction<number>>;

TypeScript 的には、useStateジェネリック関数 で、実際には

useState<number>(0);

という感じに解釈されていると思っておくとイメージしやすいです 🧩(DEV Community)


4-2. 文字列・真偽値の場合 🎀

同じように、文字列や真偽値も OK です。

文字列 (string)

const [name, setName] = useState(''); // ← 空文字だけど型は string
  • 初期値が ''(string)なので → namestringsetName(value: string) => void という感じの型になります

真偽値 (boolean)

const [isOpen, setIsOpen] = useState(false);
  • 初期値が false(boolean)なので → isOpenbooleansetIsOpen(value: boolean) => void

こんな プリミティブな値(number / string / boolean) のときは、 型をわざわざ書かずに、型推論に全部お任せでOK というのが、TypeScript コミュニティでもよく出てくるおすすめパターンです。(Reddit)


4-3. 型推論の流れを図で見てみる 📊

Mermaid でイメージ図を書いてみます 🎨

c 「最初の 1 回だけ手がかり(初期値)を渡すと、あとは TypeScript が全部広げてくれる」 というイメージで OK です 🌟


5️⃣ 型推論に任せていいケース / 危ないケース ⚠️

✅ 型推論に任せていいケース

  • 初期値がシンプルな number / string / boolean のとき

    • useState(0)
    • useState('')
    • useState(false)
  • 初期値がちゃんと中身のあるオブジェクトのとき

const [user, setUser] = useState({
name: 'Alice',
age: 20,
});

この場合、TypeScript は

{ name: string; age: number }

というオブジェクト型を自動で推論してくれます 🎉


⚠️ 注意が必要なケース(チラ見せだけ)

詳しくは 次の第34章 でやりますが、「型推論に丸投げすると危険」なパターンもあります。(Total TypeScript)

  • useState() ← 初期値を渡さない

    • TypeScript は「undefined なんだね」と解釈してしまう
  • useState([]) ← 中身がない配列

    • 型が never[] になってしまって、あとから要素を追加しにくい

こういう 「中身が空っぽ」な初期値 のときは、 ちゃんと明示的に型を書く方が安全です(それが次の章のテーマです ✨)。

この章では、まず:

「シンプルな初期値があるときは、型推論させると楽で安全」

という感覚だけつかんでおけば OK です 💪


6️⃣ 手を動かそう①:カウンターで型推論を感じる 🧮

App.tsx をこんな感じにしてみてください 👇

import { useState } from 'react';

function App() {
const [count, setCount] = useState(0);

const handleIncrement = () => {
// VS Code で prev にカーソルを合わせてみてね!
setCount((prev) => prev + 1);
};

return (
<div style={{ padding: '16px' }}>
<h1>カウンター 🎯</h1>
<p>いまのカウント: {count}</p>
<button onClick={handleIncrement}>+1 する</button>
</div>
);
}

export default App;

ポイント 💡

  • useState(0)countnumber
  • アップデート関数 setCount((prev) => prev + 1)prev自動で number と推論されます(初期値から推論された state の型を元に)。(codeparrot.ai)

VS Code で prev にマウスを乗せてみると、number と出てるはずです 👀


7️⃣ 手を動かそう②:ニックネーム入力 ✏️😺

次は文字列で型推論してみましょう。

import { useState } from 'react';

function NicknameForm() {
const [nickname, setNickname] = useState('');

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setNickname(event.target.value);
};

return (
<div style={{ padding: '16px' }}>
<h2>ニックネームフォーム 💌</h2>
<input
type="text"
value={nickname}
onChange={handleChange}
placeholder="ニックネームを入力してね"
/>
<p>こんにちは、{nickname || '???'} さん 👋</p>
</div>
);
}

export default NicknameForm;

ここでも、型は勝手に決まっています:

  • nicknamestring
  • setNickname(value: string) => void
  • setNickname の引数に 123 とか渡そうとすると、ちゃんとエラーになってくれます(TypeScript えらい 🥹)。(JavaScript Ramblings)

8️⃣ ちょっとだけ深掘り:setState のコールバックも推論される 📈

さっきのカウンターのように、setState関数 を渡すパターンがありました:

setCount((prev) => prev + 1);

このとき TypeScript は、

  • prev の型 = count と同じ型

として扱ってくれます。 countnumber なら、prevnumber と推論されます。(codeparrot.ai)

なので、もし prev.toUpperCase() みたいなことを書いたら、

  • number にそんなメソッドないよ!」

とちゃんと怒ってくれます 😂

「state の型が決まると、そこから連鎖的に他の部分の型も決まっていく」 これが型推論の気持ちいいところです ✨


9️⃣ まとめ:この章で覚えておきたいこと 📝🌈

  • useState は、渡した 初期値から state の型を推論してくれる

    • useState(0)number
    • useState('')string
    • useState(false)boolean
  • プリミティブな初期値があるときは、型を書かずに型推論に任せてOK

  • setState((prev) => ...)prev も、state の型から自動で決まる

  • 逆に、初期値が null / undefined / 空配列 / 空オブジェクト などのときは → 型推論だけだと危険なことがあるので、 → 次の第34章で「明示的な型指定」を学ぶ


次の章では、

useState<string | null>(null) みたいに、ちゃんと自分で型を指定したいとき」

の書き方をじっくりやっていきます 💻📚 ここまでで、まずは「型推論に任せてラクするとこ」と「任せちゃダメそうな匂い」が少しわかれば大成功です〜 🙌💞