Збирачі (Vite, webpack) дозволяють імпортувати не лише код: import logo from './logo.svg'. TypeScript про такі файли нічого не знає і повідомляє «Cannot find module './logo.svg'».
Рішення - ambient-оголошення модуля з шаблоном:
// src/assets.d.ts
declare module '*.svg' {
const src: string;
export default src;
}
declare module '*.module.css' {
const classes: Readonly<Record<string, string>>;
export default classes;
}
Тепер будь-який імпорт, що закінчується на .svg, має тип рядка (URL файлу).
Vite вже має такі оголошення - досить підключити їх: "types": ["vite/client"] у tsconfig або /// <reference types="vite/client" /> у файлі vite-env.d.ts. Там описано і ?url, ?raw, ?inline, і import.meta.env.
Чому declare module '*.svg' інколи «не працює». Найчастіша причина - файл з оголошенням є модулем: у ньому є import чи export на верхньому рівні.
// assets.d.ts - НЕ спрацює
import type { FC } from 'react';
declare module '*.svg' {
const Component: FC;
export default Component;
}
У файлі-модулі declare module 'назва' - це доповнення (augmentation) існуючого модуля, а доповнити шаблон, якого немає, неможливо. Ambient-оголошення нових модулів мають бути у скрипті - .d.ts без імпортів на верхньому рівні. Якщо тип потрібен з іншого пакета - імпорт всередині блоку:
declare module '*.svg?react' {
import type { FC, SVGProps } from 'react';
const Component: FC<SVGProps<SVGSVGElement>>;
export default Component;
}
Інші причини:
- файл з оголошеннями не входить у
includeчиfilesуtsconfig; - шаблон не збігається (
'*.svg'проти імпорту з?reactна кінці).
Обережно з «заглушками»: declare module 'some-lib'; без тіла робить усі імпорти з пакета типу any - це прибирає помилку, але й будь-яку перевірку. Для бібліотек краще знайти чи написати справжні типи.