React と TypeScript を使った開発をしていると、一度は遭遇する問題があります。
それは「あれ?なんでエラーになるんだろう?」と首をかしげながら、ふと気づく .ts
と .tsx
の違い。(私だけ???🥹)
この記事は、何度も同じミスを繰り返してしまう私自身への自戒の意味も込めて書いています。
またやってしまった!よくある状況
いつものように新しいコンポーネントを作成しようと思い、ファイルを作成...
// Header.ts <- 気づきましたか?
import React from 'react';
const Header = () => {
return (
<div>
<h1>Welcome to my website</h1>
</div>
);
};
export default Header;
保存して...そして待っているのは赤い波線とエラーメッセージ
error TS17004: Cannot use JSX unless the '--jsx' flag is provided.
あるいは
error TS1005: '<' expected.
「あ、また .ts で作っちゃった...」
なぜ起こるのか?シンプルな説明
React と TypeScript の世界では、拡張子に明確な役割分担があります。
-
.ts
- 普通の TypeScript ファイル(JSX は使えない) -
.tsx
- JSX が使える TypeScript ファイル
これは単なる慣習ではなく、TypeScript コンパイラが異なる方法でこれらのファイルを処理するためです。
<Header />
vs Header({})
の謎
面白いことに、.ts
ファイル内では <Header />
というJSX記法は使えませんが、同じコンポーネントを Header({})
という関数呼び出しの形で使うことはできます。
なぜでしょう?簡単に言えば、
-
<Header />
は特殊な JSX 構文で、.tsx
ファイルでのみ解釈できる -
Header({})
は普通の関数呼び出しなので、どのファイルでも問題なく動作する
からです。
import { Header } from './components/Header';
// エラー: .tsファイルではJSX構文は使えない
const jsx = <Header title="Hello" />;
// OK: 関数呼び出しは普通に動作する
const func = Header({ title: "Hello" });
解決方法
問題に気づいたら、解決法はいくつかあります。
1. ファイル拡張子を .tsx
に変更する(推奨)
最も簡単で適切な解決方法は、JSX を含むファイルの拡張子を .ts
から .tsx
に変更することです。
Header.ts → Header.tsx
これにより、TypeScript コンパイラは正しく JSX 構文を解析できるようになります。
2. tsconfig.json で JSX サポートを設定する
tsconfig.json で JSX のサポートを有効にすることも可能ですが、これはプロジェクト全体に影響します。
{
"compilerOptions": {
"jsx": "react",
// その他の設定...
}
}
3. React.createElement
を直接使用する
JSX はコンパイル時に React.createElement
の呼び出しに変換されます。JSX を使用せずに直接これを記述することもできますが、可読性が大幅に低下します。
import React from 'react';
const Header = () => {
return React.createElement(
'div',
null,
React.createElement('h1', null, 'Welcome to my website')
);
};
export default Header;
まとめ:私の自戒メモ
TypeScript と React を使う限り、この問題は付きまとう可能性があります。
気づかずに何度も同じミスを繰り返さないよう、「ファイルの拡張子に注意する」ことを自分自身への戒めにします...😣
この単純なルールを忘れないことで、「なんでJSXが使えないんだ?」とデバッグに時間を浪費することもなくなるでしょう。
あなたも私と同じミスで悩んでいたら、今日からは ファイルの拡張子 を意識して、快適な React + TypeScript 開発を楽しみましょう!