はじめに
StackBlitzでReactの学習をしていたところ、下記のように型をimportしようとした際にエディタ上でエラーが表示され、あれ?となりました。
import { User } from '../types/user';
エラーメッセージは以下で、
'User' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.
importしようとした型は以下の通りです。
export type User = {
id: number;
name: string;
email: string;
address: string;
};
結論
import側でも、import type {...}と、型をインポートすることを明示的に書くことでエラーが解消されました。
import type { User } from '../types/user';
原因
verbatimModuleSyntaxとは?
今回のエラーは、verbatimModuleSyntaxというオプションが有効になっている場合のみ発生するエラーでした。
これが何かというと、トランスパイル、つまりはTypeScript -> JavaScriptへの変換について設定できるオプションで、tsconfigファイルに記載されていました。
"verbatimModuleSyntax": true,
まず前提として、TypeScript->JavaScriptへのコンパイルでは、型の情報はすべて削除(ツリーシェイキング)された上で純粋なJavaScriptだけが残るようになります。
しかし、このオプションを有効にすると、import/export文については、 書いたままで出力されるようになります。
つまり、import type {...}という構文で書いたのであれば、そのまま削除されることなくimport typeの構文のままでコードがビルドツールに渡るようになります。
ここで、現代のViteやNext.jsなどのビルドツールでは、コンパイル時に"type"の記述がある方が高速に削除すべきコードを判断できるため、import typeの記述が残っている方が高速にコンパイルできるという仕組みになっているようです。
つまり、verbatimModuleSyntaxが有効になっていた方が都合が良いということになります。
エラーメッセージの原因
もしimport typeと明示せずに、import { User } from './types/user' と書いたままJSに出力されると、ブラウザは ./user.ts から 実体を探そうとします。
しかし、user.ts の中身が型定義だけだった場合、コンパイル後のuser.jsは空っぽ(あるいは存在しない)ため、実行時にエラーになってしまいます。
これを避けるために、「消してほしいなら import type と書いて明示しなさい」 というエラーメッセージが出るようになっている。ということになります。
終わりに
修正自体はimport側でもtypeをつければいいんだなと簡単に分かる内容でしたが、調べてみるとその背景にあるTypeScriptのコンパイル方式についてが面白かったので記事にしてみました。
この辺りはパフォーマンスや開発体験を向上させる際に、知っておくと良さそうなので色々調べてみると面白そうです。
参考記事
https://zenn.dev/hayato94087/books/b174f8b1cd80db/viewer/v00-05-16-kj8s5o8gppf5
https://qiita.com/uhyo/items/c33489155e1817479948