Зачем нужно ключевое слово `declare` в TypeScript? Объясните на примерах
Что такое `declare` и зачем он нужен?
Ключевое слово declare в TypeScript — это механизм объявления окружения (ambient declaration). Оно сообщает компилятору: "Эй, эта переменная, функция или класс существуют в рантайме, но TypeScript не должен генерировать для них JavaScript-код. Просто знай, что они есть, и не выдавай ошибки типов".
Главное отличие от обычного объявления (let, const, function) в том, что declare не создаёт никакого кода. Если вы напишете:
// Обычное объявление — TypeScript сгенерирует JS-код
var $: any; // В JS появится: var $;
// Объявление с declare — JS-код не генерируется
declare var $: any; // В JS ничего не добавится
Во втором случае вы просто описываете тип для уже существующего объекта (например, подключённого через CDN). Если бы вы использовали let $: any, TypeScript переопределил бы переменную, что может привести к конфликтам.
Где используется `declare`?
- Глобальные переменные (например,
$от jQuery,processот Node.js,__DEV__от Webpack). - Внешние библиотеки без типов — вы описываете их API вручную.
- Файлы деклараций (
.d.ts) — основной способ описания типов для JS-библиотек.
Примеры использования
1. Глобальные переменные из CDN
// global.d.ts
declare var $: (selector: string) => JQuery;
// main.ts — теперь можно использовать $ без ошибок
$('.my-class').hide();
2. Описание модуля без типов
// lodash.d.ts
declare module 'lodash' {
export function chunk(array: T[], size: number): T[][];
}
// app.ts
import { chunk } from 'lodash'; // Работает, хотя у lodash нет типов
3. Расширение существующих типов
// Добавляем свойство к глобальному window
declare global {
interface Window {
myCustomVar: string;
}
}
// Теперь можно писать:
window.myCustomVar = 'hello';
Почему `.d.ts` файлы?
Файлы с расширением .d.ts — это "чистые" декларации. Они содержат только declare и не могут содержать реализаций. Это контракт: "Вот как выглядит API библиотеки, а реализация уже есть в JS". Обычные .ts файлы — это код, который компилируется. .d.ts — это только типы.
Итог: declare — это мост между миром JavaScript (где типов нет) и TypeScript (где типы обязательны). Он позволяет использовать существующий JS-код с полной проверкой типов, не генерируя лишнего кода.
Дополнение: `declare` и модули
Важно понимать, что declare работает по-разному в глобальном контексте и внутри модулей. Если ваш файл — модуль (есть import или export), то объявление declare var $ не сделает $ глобальной. Вам нужно использовать declare global.
Пример для модуля:
// utils.ts (это модуль)
export function doSomething() {
// Ошибка! $ не определена в этом контексте
// $('div').hide();
// Правильно: расширяем глобальный скоуп
declare global {
var $: (selector: string) => any;
}
// Теперь $ доступна
$('div').hide();
}
Также declare часто используется с namespace для организации сложных типов:
declare namespace MyLib {
function init(): void;
interface Config {
debug: boolean;
}
}
Это позволяет вызывать MyLib.init() без импорта, если библиотека подключена глобально. Главное правило: если сущность существует в рантайме, но TypeScript о ней не знает — используйте declare.