Next.js で SVG をインポートしたら型が any になる問題を解決する
🌼 はじめに
最近 Next.js の開発で SVG を扱うことがありますが、インポートした SVG の型が any
になってめんどくさいなと思ってました。
例えば、以下のように SVG をインポートして、オブジェクトや配列の値として入れます。
import UserIcon from "@/assets/svg/user_icon.svg"
type ComponentProps = { userName: string }
const Component = ({ userName }: ComponentProps) => {
const user = {
userName,
icon: UserIcon
} // user の型が { userName: string; icon: any } になってしまう
// ...
}
この状態だと user.icon
の型は any
に推論されます。もし他のところで user.icon
の型で React.FC<React.SVGProps<SVGElement>>
を期待してるなら、型があわなくてエラーになるでしょう。
仕方ないので形をアサーションするとなんとかなりますが、、
const user = {
userName,
icon: UserIcon as React.FC<React.SVGProps<SVGElement>>
}
SVG 使う度に型アサーションしないといけないって絶対めんどくさい!と思って自動で型がつくようにしました。その解決案を共有します。
+) ちなみに Next.js は App Router(14.0.4)を使ってました。
any
になるのか
1. 原因: なぜ SVG の型が そもそも、なぜ SVG をインポートしたら型が any になるかというと、Next.js が SVG の型を any に指定しているからです。
Next.js + TypeScript のプロジェクトには、ルートに next-env.d.ts
というファイルがあると思います。これは TypeScript のコンパイラーが Next.js の型を認識できるようにしるファイルです。
そして、その next-env.d.ts
で next/image-types/global.d.ts
という型ファイルを呼んでおり、その中に SVG の型が定義されています。
「プラグインとのコンフリクトを避けるために any
を使ってる」というコメントもあるので、意図的に any
にしてることは間違いないです。
2. 解決: カスタムの型定義ファイルを追加する
残念ながら next-env.d.ts
は自動生成されるので、このファイルを直接変更しても新しく自動生成されたら変更内容が消えるのでカスタムすることはできません。
でも方法はあります。カスタムの型定義ファイルを追加することです。
まずは、プロジェクトのルートに新しい型定義ファイルを追加します。私は svg.d.ts
という名前にしましたが、ファイル名は何でも大丈夫です。
そしてそのファイルに、SVG の型情報を書きます。
declare module "*.svg" {
const content: React.FC<React.SVGProps<SVGElement>>
export default content
}
そして tsconfig.json
の "include"
配列に、svg.d.ts
を追加します。
注意点は、必ず svg.d.ts
をnext-env.d.ts
より前におくことです。next-env.d.ts
より後においたらちゃんと反映されず、SVG
が any のままでした。
{
"include": [
+ "svg.d.ts",
"next-env.d.ts",
".next/types/**/*.ts",
"**/*.ts",
"**/*.tsx"
],
"exclude": ["node_modules"]
}
そしてキャッシュ消したり再ビルドしたりして、新しい型を反映させます。
これで SVG をインポートしたら型がちゃんと React.FC<React.SVGProps<SVGElement>>
になってました。やったー!
🌷 終わり
型定義カスタムは別の場面でも使えそうな気がします。めんどくさがってよかった。
Discussion