Зачем TypeScript ключевое слово declare?
Представьте, что вы строите дом, но некоторые комнаты уже существуют — их построил кто-то другой (например, JavaScript-библиотека). Вы не можете зайти туда и перестраивать стены, но вам нужно знать их планировку, чтобы правильно разместить мебель. Именно эту роль и выполняет ключевое слово declare в TypeScript.
Если вы когда-нибудь работали с популярными библиотеками (jQuery, Lodash, D3.js), то наверняка сталкивались с файлами .d.ts — это и есть "планировки", созданные с помощью declare. В этой статье мы разберем, что такое declare, зачем он нужен и как его использовать на практике. Готовьтесь — будет много кода и мало воды.
Что такое declare в TypeScript?
declare — это ключевое слово, которое сообщает компилятору TypeScript: "Эй, эта штука (переменная, функция, класс или модуль) существует, но она определена где-то в другом месте. Просто поверь мне на слово и дай использовать её в коде."
Важно понимать: declare не генерирует никакого JavaScript-кода. Это чисто информационная конструкция для компилятора. Если вы напишете declare const x: number = 5, то после компиляции x исчезнет — его не будет в итоговом файле.
Когда это нужно?
- Работа с глобальными переменными — например,
window,documentили переменные, добавленные через CDN. - Типизация JavaScript-библиотек — когда библиотека написана на чистом JS, но вы хотите использовать её с TypeScript.
- Создание типов для конфигурационных файлов — CSS-модули, изображения, JSON-файлы.
Синтаксис declare: базовые примеры
1. Декларация переменных
Допустим, у вас есть глобальная переменная VERSION, которая определена в HTML-файле через <script>. TypeScript о ней ничего не знает, поэтому вы пишете:
declare const VERSION: string;
console.log(VERSION); // OK, TypeScript знает, что это строка
После компиляции declare const VERSION: string исчезнет, а console.log(VERSION) останется.
2. Декларация функций
Представьте, что у вас есть глобальная функция trackEvent из аналитики:
declare function trackEvent(eventName: string, properties?: Record<string, any>): void;
// Использование
trackEvent("button_click", { buttonId: "submit" });
Компилятор не будет проверять реализацию — он просто знает сигнатуру.
3. Декларация классов
Библиотека предоставляет класс Moment из глобального пространства имен:
declare class Moment {
constructor(date: string);
format(pattern: string): string;
}
// Использование
const now = new Moment("2024-01-01");
console.log(now.format("YYYY-MM-DD"));
4. Декларация модулей
Это самый частый сценарий. Например, у вас есть CSS-модули или JSON-файлы:
// styles.d.ts
declare module "*.css" {
const styles: Record<string, string>;
export default styles;
}
// images.d.ts
declare module "*.png" {
const src: string;
export default src;
}
Теперь вы можете импортировать CSS и изображения без ошибок TypeScript.
declare и .d.ts файлы
Файлы с расширением .d.ts (декларации типов) — это "зона обитания" ключевого слова declare. Они содержат только объявления и не компилируются в JavaScript. Обычно они лежат в папке types/ или поставляются вместе с библиотекой (например, @types/lodash).
Пример: создаем типы для jQuery
Допустим, вы подключаете jQuery через CDN и хотите использовать его в TypeScript:
// jquery.d.ts
declare function $(selector: string): any;
declare namespace $ {
function ajax(url: string, settings: any): void;
}
Теперь вы можете писать:
$("#myButton").hide();
$.ajax("/api/data", { method: "GET" });
Конечно, в реальности вы возьмете готовые типы из @types/jquery, но суть та же.
Продвинутые техники с declare
1. declare global — расширение глобальных типов
Если вам нужно добавить новое свойство к глобальному объекту (например, window), используйте declare global:
// global.d.ts
export {};
declare global {
interface Window {
myCustomProperty: string;
}
}
// Теперь в любом файле:
window.myCustomProperty = "Hello!";
2. declare module с перегрузками
Библиотека может экспортировать функцию с разными сигнатурами:
declare module "my-utils" {
export function format(input: string): string;
export function format(input: string, options: { uppercase: boolean }): string;
export function format(input: number): string;
}
3. declare для enum
Если вы используете enum из JavaScript-библиотеки:
declare enum Color {
Red = "RED",
Green = "GREEN",
Blue = "BLUE"
}
Распространенные ошибки и как их избежать
Ошибка 1: declare не заменяет реализацию
Запомните: declare — это обещание, что что-то существует. Если вы напишете declare function add(a: number, b: number): number, но не предоставите реализацию в JavaScript, в рантайме будет ошибка.
Ошибка 2: Избыточное использование declare
Не нужно писать declare для переменных, которые уже объявлены в вашем TypeScript-коде. Это вызовет дублирование и путаницу.
Ошибка 3: Забыть про export в модулях
Если вы используете declare module "something", не забудьте указать, что именно экспортируется:
// Неправильно
declare module "config" {
const apiUrl: string;
}
// Правильно
declare module "config" {
export const apiUrl: string;
}
Практический пример: типизация старой JS-библиотеки
Представьте, что у вас есть библиотека old-lib.js, которая добавляет глобальный объект OldLib