declare в TypeScript: Зачем он нужен и как его использовать

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

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

Начать курс

Зачем 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

Блоги

Рекомендаций по книгам