Введение: "Cannot find module" — знакомая боль?
Вы пишете на TypeScript, добавляете в проект красивую иконку в формате SVG и пытаетесь импортировать её стандартным способом:
import Logo from './assets/logo.svg';
И тут TypeScript выдаёт вам красную ошибку:
Cannot find module './assets/logo.svg' or its corresponding type declarations.
Знакомо? Если вы хоть раз сталкивались с этой проблемой — вы не одиноки. Вопрос "Unable to import svg files in typescript" на Stack Overflow собрал тысячи просмотров и десятки ответов. Проблема в том, что TypeScript по умолчанию не знает, как обрабатывать не-кодовые файлы (SVG, PNG, CSS).
В этой статье мы разберём полное объяснение с кодом: почему возникает ошибка и как её исправить раз и навсегда. Вы узнаете про declaration files, настройку Webpack и Vite, а также про то, как правильно типизировать SVG в React-проектах.
Почему TypeScript не видит SVG?
TypeScript — это строго типизированный язык. Он проверяет типы на этапе компиляции. Когда вы пишете import Logo from './logo.svg', TypeScript ожидает, что модуль ./logo.svg экспортирует что-то с известным типом. Но SVG — это не TypeScript-файл, это XML. У TypeScript нет встроенного описания для SVG-модулей.
Отсюда и ошибка: компилятор не может найти type declarations (объявления типов) для этого файла.
Существует три основных способа решить проблему:
- Способ 1: Создать глобальный файл объявлений (
.d.ts) - Способ 2: Использовать Webpack/Vite с правильными загрузчиками
- Способ 3: Импортировать SVG через
urlилиraw(для специфических случаев)
Мы разберём каждый способ с примерами кода.
Способ 1: Глобальный declaration file (самый популярный)
Это решение из Stack Overflow, которое работает в 90% случаев. Вы создаёте файл с расширением .d.ts (например, src/declarations.d.ts или src/svg.d.ts) и объявляете модуль для SVG.
Вот минимальный код:
// src/declarations.d.ts
declare module '*.svg' {
const content: string;
export default content;
}
Что здесь происходит? Мы говорим TypeScript: "Любой файл с расширением .svg — это модуль, который экспортирует по умолчанию строку (путь к файлу или содержимое)".
Если вы используете React и хотите импортировать SVG как React-компонент (через @svgr/webpack или vite-plugin-svgr), объявление будет другим:
// src/declarations.d.ts
declare module '*.svg' {
import React from 'react';
const SVGComponent: React.FC<React.SVGProps<SVGSVGElement>>;
export default SVGComponent;
}
Важно: Убедитесь, что файл .d.ts включён в ваш tsconfig.json. Обычно достаточно, чтобы он лежал внутри src/, но если нет — добавьте путь в поле include:
// tsconfig.json
{
"compilerOptions": {
// ... остальные настройки
},
"include": ["src/**/*"]
}
После этого ошибка "Cannot find module" исчезнет, и вы сможете импортировать SVG как обычно.
Способ 2: Настройка Webpack или Vite
Глобальное объявление — это только половина дела. Чтобы импорт реально работал в рантайме (в браузере), нужно настроить сборщик.
Для Webpack
Установите file-loader или url-loader:
npm install --save-dev file-loader
Добавьте правило в webpack.config.js:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.svg$/,
use: ['file-loader']
}
]
}
};
Если вы хотите импортировать SVG как React-компонент, используйте @svgr/webpack:
npm install --save-dev @svgr/webpack
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.svg$/,
use: ['@svgr/webpack', 'file-loader']
}
]
}
};
Теперь вы можете писать:
import { ReactComponent as Logo } from './logo.svg';
// или
import Logo from './logo.svg?component';
Для Vite
Vite из коробки поддерживает импорт SVG как URL. Просто используйте:
import Logo from './logo.svg?url'; // строка с URL
Для импорта как строки (содержимого SVG):
import Logo from './logo.svg?raw'; // строка с SVG-кодом
Чтобы импортировать SVG как React-компонент, установите плагин vite-plugin-svgr:
npm install --save-dev vite-plugin-svgr
// vite.config.ts
import { defineConfig } from 'vite';
import svgr from 'vite-plugin-svgr';
export default defineConfig({
plugins: [svgr()]
});
После этого вы сможете писать:
import Logo from './logo.svg?react';
И не забудьте обновить declaration file для поддержки суффикса ?react:
declare module '*.svg?react' {
import React from 'react';
const component: React.FC<React.SVGProps<SVGSVGElement>>;
export default component;
}
Способ 3: Импорт как URL или raw-строка
Иногда вам не нужен компонент, а только путь к файлу или его содержимое. TypeScript может это поддержать, если вы правильно объявите модули.
Для URL:
declare module '*.svg?url' {
const url: string;
export default url;
}
Для raw (текстовое содержимое):
declare module '*.svg?raw' {
const content: string;
export default content;
}
Использование:
import svgUrl from './icon.svg?url';
import svgContent from './icon.svg?raw';
console.log(svgUrl); // /assets/icon.abc123.svg
console.log(svgContent); // <svg>...</svg>
Частые ошибки и их решения
- Ошибка: "File is not included in tsconfig"