新人エンジニアが最初にハマる Next.js の型エラー10選
はじめに
Next.js と TypeScript の組み合わせはとても強力ですが、最初に触れるエンジニアにとって「型エラー地獄」に陥ることも少なくありません。
この記事では、実際の開発現場で新人エンジニアがよく遭遇する型エラーを10個ピックアップし、原因と解決方法をわかりやすく解説します。
1. useState 初期値の型推論ミス
const [count, setCount] = useState();
setCount(1);
エラー: Argument of type 'number' is not assignable to parameter of type 'undefined'.
原因: 初期値を省略すると useState は undefined 型として推論する。
解決: 明示的に型か初期値を与える。
const [count, setCount] = useState<number>(0);
2. useEffect の依存配列に関数を入れてはいけない
useEffect(() => {
doSomething();
}, [doSomething]);
エラー: React Hook useEffect has a missing dependency: 'doSomething'.
原因: 関数が再生成されるたびに依存関係が変わるため。
解決: useCallback で関数をメモ化。
const doSomething = useCallback(() => { ... }, []);
3. props の型を定義し忘れる
export default function Hello(props) {
return <p>{props.name}</p>;
}
エラー: Property 'name' does not exist on type '{}'
解決:
type HelloProps = { name: string };
export default function Hello({ name }: HelloProps) {
return <p>{name}</p>;
}
4. useRouter の query は常に string | string[] | undefined
const { query } = useRouter();
const id = query.id; // 型エラー
解決:
const id = Array.isArray(query.id) ? query.id[0] : query.id ?? "";
5. params 型の不一致(App Router)
export default function Page({ params }: { params: { id: number } }) {
return <div>{params.id}</div>;
}
エラー: "number" is not assignable to type "string"
原因: ルートパラメータは常に string として渡される。
解決:
export default function Page({ params }: { params: { id: string } }) {
return <div>{params.id}</div>;
}
6. APIレスポンスの型ミスマッチ
const res = await fetch("/api/data");
const data: DataType = await res.json();
エラー: Type 'any' is not assignable to type 'DataType'.
解決: as を使うか、Zodなどで型を検証。
const data = (await res.json()) as DataType;
7. children の型を未定義
const Layout = ({ children }) => <main>{children}</main>;
エラー: Property 'children' does not exist on type '{}'
解決:
type LayoutProps = { children: React.ReactNode };
const Layout = ({ children }: LayoutProps) => <main>{children}</main>;
8. map の key に number を直接使う
{items.map((item, i) => (
<div key={i}>{item.name}</div>
))}
警告: Do not use array index as key
対策: 一意なIDを使う。
{items.map(item => <div key={item.id}>{item.name}</div>)}
9. 非同期関数の戻り値を直接コンポーネントに使う
const data = fetchData();
return <p>{data.title}</p>;
エラー: Property 'title' does not exist on type 'Promise<Data>'.
解決:
const [data, setData] = useState<Data | null>(null);
useEffect(() => { fetchData().then(setData); }, []);
10. useRef 初期値に null を与えない
const inputRef = useRef<HTMLInputElement>();
inputRef.current?.focus();
エラー: Expected 1 arguments, but got 0.
解決:
const inputRef = useRef<HTMLInputElement>(null);
まとめ
TypeScript のエラーは「なぜ型がそう推論されたか」を理解すると一気に楽になります。
型を“敵”ではなく“相棒”として扱えるようになると、Next.js の開発体験は劇的に向上します。