TypeScript изначально понимает только JavaScript/TypeScript-модули. При попытке импортировать SVG, PNG, CSS, SCSS или другие нестандартные ресурсы компилятор выдаёт ошибку:
Cannot find module './logo.svg'
Webpack способен обрабатывать подобные файлы через loaders, asset modules и плагины, однако TypeScript ничего не знает о механизме сборки. Для него такие импорты остаются неизвестными.
Проблема решается через декларации модулей (*.d.ts),
которые объясняют TypeScript:
Без деклараций:
Когда встречается импорт:
import logo from './logo.svg';
TypeScript:
logo.svg.ts;logo.svg.d.ts;node_modules;Если подходящая декларация отсутствует — появляется ошибка.
Webpack здесь не участвует. Проверка выполняется ещё до сборки.
Обычно создаётся отдельный файл:
src/types/assets.d.ts
или:
global.d.ts
Внутри объявляются виртуальные модули.
Пример:
declare module '*.png' {
const value: string;
export default value;
}
Теперь TypeScript понимает:
import image from './image.png';
и знает, что image — строка.
declare module '*.png' {
const src: string;
export default src;
}
Использование:
import logo from './logo.png';
const img = document.createElement('img');
img.src = logo;
declare module '*.jpg' {
const src: string;
export default src;
}
declare module '*.jpeg' {
const src: string;
export default src;
}
declare module '*.gif' {
const src: string;
export default src;
}
declare module '*.webp' {
const src: string;
export default src;
}
declare module '*.avif' {
const src: string;
export default src;
}
declare module '*.ico' {
const src: string;
export default src;
}
SVG — особый случай. Его можно использовать несколькими способами.
Если Webpack обрабатывает SVG как asset/resource:
declare module '*.svg' {
const src: string;
export default src;
}
Импорт:
import icon from './icon.svg';
img.src = icon;
Webpack вернёт URL:
/assets/icon.a1b2c3.svg
При использовании @svgr/webpack SVG превращается в
React-компонент.
Webpack:
{
test: /\.svg$/,
use: ['@svgr/webpack']
}
Декларация:
declare module '*.svg' {
import * as React from 'react';
export const ReactComponent: React.FC<
React.SVGProps<SVGSVGElement>
>;
const src: string;
export default src;
}
Использование:
import { ReactComponent as Logo } from './logo.svg';
export const Header = () => {
return <Logo width={120} />;
};
Современные конфигурации часто поддерживают оба варианта:
import iconUrl from './icon.svg';
import { ReactComponent as Icon } from './icon.svg';
В этом случае декларация должна содержать:
Полный вариант:
declare module '*.svg' {
import * as React from 'react';
export const ReactComponent: React.FC<
React.SVGProps<SVGSVGElement>
>;
const content: string;
export default content;
}
Если CSS импортируется ради побочного эффекта:
import './styles.css';
можно использовать пустую декларацию:
declare module '*.css';
Этого достаточно.
Если используется CSS Modules:
.button {
color: red;
}
Импорт:
import styles from './Button.module.css';
TypeScript должен понимать структуру объекта.
Минимальная декларация:
declare module '*.module.css' {
const classes: {
[key: string]: string;
};
export default classes;
}
Теперь доступно:
styles.button
Конструкция:
[key: string]: string;
не обеспечивает строгую типизацию.
TypeScript разрешит:
styles.unknownClass
даже если класса не существует.
Для строгой типизации используют генераторы:
Пример автоматически сгенерированного файла:
declare const styles: {
readonly button: string;
readonly active: string;
};
export default styles;
Теперь TypeScript обнаруживает ошибки:
styles.unknown;
Ошибка:
Property 'unknown' does not exist
declare module '*.scss';
declare module '*.module.scss' {
const classes: {
[key: string]: string;
};
export default classes;
}
declare module '*.sass' {
const classes: {
[key: string]: string;
};
export default classes;
}
declare module '*.less';
declare module '*.module.less' {
const classes: {
[key: string]: string;
};
export default classes;
}
declare module '*.woff' {
const src: string;
export default src;
}
declare module '*.woff2' {
const src: string;
export default src;
}
declare module '*.ttf' {
const src: string;
export default src;
}
declare module '*.eot' {
const src: string;
export default src;
}
declare module '*.mp4' {
const src: string;
export default src;
}
declare module '*.webm' {
const src: string;
export default src;
}
declare module '*.mp3' {
const src: string;
export default src;
}
declare module '*.wav' {
const src: string;
export default src;
}
Иногда создаётся единый файл:
declare module '*.png';
declare module '*.jpg';
declare module '*.jpeg';
declare module '*.gif';
declare module '*.svg';
declare module '*.scss';
declare module '*.css';
Подход уменьшает объём кода, но имеет недостатки:
TypeScript должен видеть .d.ts файлы.
Обычно они автоматически включаются через:
{
"include": [
"src"
]
}
Если декларации лежат вне src:
{
"include": [
"src",
"types"
]
}
Иногда применяется отдельная папка типов:
types/
assets.d.ts
Конфигурация:
{
"compilerOptions": {
"typeRoots": [
"./types",
"./node_modules/@types"
]
}
}
Добавляет файлы в программу TypeScript.
{
"include": ["src", "types"]
}
Подключаются:
.ts;.tsx;.d.ts.Определяет директории поиска глобальных типов.
{
"typeRoots": [
"./types"
]
}
После указания typeRoots TypeScript перестаёт
автоматически искать типы вне перечисленных директорий.
Это частая причина ошибок:
Cannot find type definition file
Файл .d.ts может быть:
declare module '*.png' {
const src: string;
export default src;
}
Работает глобально.
Если добавить:
export {};
файл становится модулем.
Некоторые глобальные декларации могут перестать работать.
Ошибка:
Duplicate identifier
возникает, когда:
.d.ts.Пример проблемы:
declare module '*.svg'
в двух разных файлах.
Типизация должна соответствовать реальному поведению Webpack.
Декларация:
declare module '*.svg' {
const src: string;
export default src;
}
Но loader возвращает React-компонент.
В результате:
Это приводит к логическим ошибкам.
Webpack 5 заменил:
Теперь используются:
Возвращает URL:
type: 'asset/resource'
Тип:
const src: string;
Возвращает base64:
data:image/png;base64,...
Тип также остаётся строкой:
const src: string;
Возвращает содержимое файла.
Например:
type: 'asset/source'
Для SVG:
declare module '*.svg' {
const content: string;
export default content;
}
Но теперь строка содержит XML-код, а не URL.
Старый подход:
use: 'raw-loader'
Импорт:
import template from './template.html';
Декларация:
declare module '*.html' {
const content: string;
export default content;
}
TypeScript умеет работать с JSON нативно.
В tsconfig.json:
{
"resolveJsonModule": true
}
После этого:
import data from './data.json';
работает без дополнительных деклараций.
Для .wasm иногда используются декларации:
declare module '*.wasm' {
const value: WebAssembly.Module;
export default value;
}
или:
declare module '*.wasm' {
const init: () => Promise<any>;
export default init;
}
Тип зависит от конфигурации Webpack и wasm-loader.
Распространённая структура:
src/
types/
assets.d.ts
styles.d.ts
svg.d.ts
Или:
types/
global.d.ts
declare module '*.png';
declare module '*.jpg';
declare module '*.gif';
declare module '*.module.scss' {
const classes: {
[key: string]: string;
};
export default classes;
}
declare module '*.svg' {
import * as React from 'react';
export const ReactComponent: React.FC<
React.SVGProps<SVGSVGElement>
>;
const src: string;
export default src;
}
module.exports = {
module: {
rules: [
{
test: /\.module\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true
}
},
'sass-loader'
]
},
{
test: /\.svg$/,
use: [
'@svgr/webpack'
]
},
{
test: /\.(png|jpg|gif)$/i,
type: 'asset/resource'
}
]
}
};
declare module '*.module.scss' {
const classes: {
[key: string]: string;
};
export default classes;
}
declare module '*.svg' {
import * as React from 'react';
export const ReactComponent: React.FC<
React.SVGProps<SVGSVGElement>
>;
const src: string;
export default src;
}
declare module '*.png' {
const src: string;
export default src;
}
declare module '*.jpg' {
const src: string;
export default src;
}
declare module '*.gif' {
const src: string;
export default src;
}
Причины:
.d.ts;Причина:
Причина:
Причины:
.d.ts.В production-проектах обычно:
.d.ts;declare module '*'.Строгая типизация ресурсов особенно важна в: