Как в TypeScript явно добавить новое свойство в глобальный объект `window`?
Правильное расширение интерфейса Window
Проблема в том, что TypeScript имеет строгую типизацию для глобального объекта window. Чтобы добавить своё свойство, нужно расширить существующий интерфейс Window с помощью декларации слияния (declaration merging).
Самый простой и рекомендуемый способ — создать файл объявлений (например, global.d.ts или types.d.ts) в корне проекта и добавить туда:
// global.d.ts
interface Window {
myAppConfig: {
apiUrl: string;
debugMode: boolean;
};
}
После этого TypeScript перестанет ругаться, и вы сможете обращаться к window.myAppConfig с полной поддержкой автодополнения и типов.
Важно: Этот подход работает, потому что TypeScript автоматически объединяет несколько объявлений одного интерфейса в один общий тип.
Более гибкий вариант с использованием модулей
Если вы работаете с модулями (файлы с import/export), лучше использовать declare global:
// types.ts (или любой другой модульный файл)
export {};
declare global {
interface Window {
myAppConfig: {
apiUrl: string;
debugMode: boolean;
};
}
}
Этот способ явно указывает, что вы расширяете глобальный интерфейс из модуля. Строчка export {} обязательна, чтобы файл считался модулем.
Альтернатива: приведение типа (type assertion)
Если нужно быстрое решение без создания отдельного файла (например, для прототипа), можно использовать приведение типа:
(window as any).myAppConfig = {
apiUrl: 'https://api.example.com',
debugMode: true
};
Но этот способ не рекомендуется для production, так как вы теряете все преимущества TypeScript (автодополнение, проверку типов).
Лучшая практика
- Создайте отдельный файл
types/global.d.tsилиsrc/types/global.d.ts. - Используйте осмысленные имена для свойств, чтобы избежать конфликтов.
- Если свойство может отсутствовать, сделайте его опциональным (
myAppConfig?: ...). - Для сложных объектов вынесите тип в отдельный интерфейс:
// types.ts
export interface AppConfig {
apiUrl: string;
debugMode: boolean;
}
declare global {
interface Window {
myAppConfig?: AppConfig;
}
}
Такой подход делает код чище, переиспользуемым и легко поддерживаемым.
<<Использование namespace для организации глобальных данных
Если вы не хотите загрязнять интерфейс Window множеством свойств, можно создать глобальный namespace. Это особенно удобно, когда у вас много разных глобальных конфигураций.
// global.d.ts
declare namespace MyApp {
interface Config {
apiUrl: string;
debugMode: boolean;
}
let config: Config;
}
Затем в коде:
// Инициализация
MyApp.config = {
apiUrl: 'https://api.example.com',
debugMode: true
};
// Использование
console.log(MyApp.config.apiUrl);
Плюсы: всё глобальное хранится под одним именем, легко найти и рефакторить. Минусы: нужно быть аккуратным с порядком загрузки файлов (в браузере). На практике это решается размещением объявления в самом начале загрузки скриптов.
Этот подход хорошо сочетается с первым вариантом: можно оставить window нетронутым, а все кастомные глобальные данные хранить в своём namespace.