Cannot find module '〇〇.svg or its corresponding type declarations の解決
発生したエラー
error TS2307: Cannot find module '@/svgs/icons/〇〇.svg' or its corresponding type declarations.
import 〇〇 from "@/svgs/icons/〇〇.svg";
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
これは Next.js プロジェクトで SVG ファイルをインポートする際に、TypeScript がそのモジュールの型定義を見つけられないというエラーです。複数の SVG ファイルのインポートで同様のエラーが発生しました。
エラーの発生状況
- biome を採用しているプロジェクト
- noUndeclaredValue(React が宣言されていないというエラー)の無視コメントを削除
-
import type React from "react"
をファイルに追加していったところ発生 - このプロジェクトでは SVG をモジュール化して使うために
@svgr/webpack
を採用 - SVG の型定義ファイル(
index.d.ts
)が必要な状況
エラーの原因
結論、index.d.ts
ファイルの import 文追加が直接的な原因でした。
このエラーを考える時に必要な知識は次の 2 つです:
-
SVG ファイルの型定義の不在: TypeScript はデフォルトで SVG ファイルをモジュールとして認識しません。
-
型定義ファイルの書き方の問題: 特に重要なポイントとして、型定義ファイル(
.d.ts
)でimport
文を使うと、そのファイルはグローバル宣言ではなく通常のモジュール宣言として扱われます。
問題のindex.d.ts
ファイルのコード:
// index.d.ts
import type React from "react";
declare module "*.svg" {
const content: React.FC<React.SVGProps<SVGElement>>;
export default content;
}
この書き方だと、import
文によってファイルがモジュールとして扱われ、グローバルな型定義として適用されなくなります。
解決方法
当初は以下のようにして解決しようとしていたのですが、biome の format で import 文が削除されてしまいました。
// index.d.ts
declare module "*.svg" {
import type React from "react";
const content: React.FC<React.SVGProps<SVGElement>>;
export default content;
}
困ったので、biome-ignore コメントを残そうかと思いましたが、Stack Overflowで対処法を発見しました。
要は、import()
型構文を使用すれば回避できるというものです。以下のようにファイルを修正したところ、TypeScript のエラーが解消されました:
// index.d.ts
declare module "*.svg" {
const content: import("react").FC<import("react").SVGProps<SVGElement>>;
export default content;
}
技術的解説
TypeScript の型定義ファイル(.d.ts
)には、グローバル宣言とモジュール宣言の 2 種類があります:
-
グローバル宣言:
import
文やexport
文がないファイル。プロジェクト全体で自動的に認識される -
モジュール宣言:
import
文やexport
文があるファイル。モジュールとして扱われ、明示的にインポートする必要がある
SVG ファイルのような特殊なファイル形式に対する型定義は、一般的にグローバル宣言として提供されます。しかし、import React from "react"
のような文を入れるとモジュール宣言になってしまうため、今回のエラーが発生しました。
そこで今回使用したのがimport()
構文です。通常の import 文と異なり、この構文は型定義の中で他のモジュールの型を参照するために使用でき、ファイルのグローバル宣言としての性質を維持できます。
これによって、@svgr/webpack
の型定義がグローバルに適用され、SVG モジュールの型解決ができるようになりました。
おまけ
先輩の指摘で判明しましたが、今回の場合このように書けば解決できました。
declare module "*.svg" {
import type { FC, SVGProps } from "react";
const content: FC<SVGProps<SVGElement>>;
export default content;
}
React だと認識されないけど、個別インポートだと認識されるみたいですね。
ふーんやるじゃん。
以上です。
Discussion