React TypeScript: JSX children の型エラー解決方法
問題の概要
React TypeScript を使用している際、以下のエラーに遭遇することがあります:
This JSX tag's 'children' prop expects a single child of type 'Element | undefined', but multiple children were provided.
このエラーは、コンポーネントが単一の子要素のみを受け入れるように型定義されているのに、実際には複数の子要素が渡されている場合に発生します。
エラーの原因
元のコードでは、IInputWrapperProps
インターフェースで children
が以下のように定義されています:
export interface IInputWrapperProps {
// ... 他のプロパティ
children?: JSX.Element;
}
この定義では、children
は単一の JSX.Element
または undefined
のみを受け入れるため、複数の要素(パスワード入力フィールドと表示/非表示トグルボタン)を渡そうとすると型エラーが発生します。
解決方法
方法1: 適切な children の型定義
最も推奨される解決策は、children
の型を React.ReactNode
に変更することです:
export interface IInputWrapperProps {
label?: string;
required?: boolean;
minimizedLabel?: boolean;
description?: string;
error?: string;
wrapperStyle?: React.CSSProperties;
children?: React.ReactNode; // 変更点
}
React.ReactNode
は、以下のすべての型を含む包括的な型です:
- JSX要素
- 文字列
- 数値
- 配列(複数の要素)
- 真偽値(true/false)
- null/undefined
TIP
React.ReactNode
は React 18 以降で推奨される children の型です。React のすべての有効な子要素をカバーします。
方法2: React.PropsWithChildren の使用
別のアプローチとして、React.PropsWithChildren
ユーティリティ型を使用する方法もあります:
import React from "react";
export interface IInputWrapperProps extends React.PropsWithChildren {
label?: string;
required?: boolean;
minimizedLabel?: boolean;
description?: string;
error?: string;
wrapperStyle?: React.CSSProperties;
// children は自動的に含まれる
}
方法3: フラグメントでラップする
一時的な解決策として、複数の子要素をフラグメントでラップする方法もあります:
<InputWrapper label={label} error={error} {...rest}>
<>
<Passwordinput
label={label}
type={state ? "text" : "password"}
onChange={e => onChange(e.target.value)}
value={value}
error={error}
/>
<Svg>
<img
onClick={() => setstate(state => !state)}
style={{ cursor: "pointer" }}
src={state ? eyeShow : eyeHide}
alt="searchIcon"
/>
</Svg>
</>
</InputWrapper>
ただし、これは根本的な型定義の問題を解決しないため、最初の2つの方法の方が優れています。
よくある間違い
props の分割代入忘れ
コンポーネント定義時に props の分割代入を忘れると、このエラーが発生することがあります:
// 間違った書き方
export const MyComponent: FC<PropsWithChildren> = (children) => (
<div>{children}</div>
);
// 正しい書き方
export const MyComponent: FC<PropsWithChildren> = ({ children }) => (
<div>{children}</div>
);
コメントによる意図しない子要素
JSX内のコメントが意図せず子要素として認識されることがあります:
// 問題が発生する可能性のあるコード
<div>
{renderTabs()}
{/* TODO: 保留中の作業 */}
{renderButtons()}
</div>
修正後のコード例
修正後の InputWrapper
コンポーネント:
export interface IInputWrapperProps {
label?: string;
required?: boolean;
minimizedLabel?: boolean;
description?: string;
error?: string;
wrapperStyle?: React.CSSProperties;
children?: React.ReactNode; // React.ReactNode を使用
}
export default ({
label,
error,
description,
children,
required,
wrapperStyle,
minimizedLabel
}: IInputWrapperProps) => {
return (
<ErrorBoundary id="InputWrapperErrorBoundary">
<div style={wrapperStyle}>
<Container>
<Label minimized={minimizedLabel}>
{label} {required && <span style={{ color: "red" }}> *</span>}
</Label>
{children}
</Container>
{description && <Description>{description}</Description>}
{error && <Error>{error}</Error>}
</div>
</ErrorBoundary>
);
};
まとめ
React TypeScript で children
関連の型エラーが発生した場合:
React.ReactNode
を使用して children の型を定義するReact.PropsWithChildren
ユーティリティ型を活用する- 複数の子要素を渡す必要がある場合は、適切な型定義を確認する
- コンポーネントの props 分割代入を正しく行う
これらの対策により、型安全性を保ちながら柔軟なコンポーネント設計が可能になります。