TypeScript не видит SVG? Решаем проблему раз и навсегда

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: "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>

Частые ошибки и их решения