Інколи значення справді глобальне: дані, які сервер вбудовує в сторінку (window.App = {...} з Blade), скрипт аналітики, змінні середовища збирача. TypeScript треба про них розповісти.
Доповнення Window з файлу-модуля:
// src/types/global.d.ts
import type { User } from '../models';
declare global {
interface Window {
App: {
locale: string;
user: User | null;
};
dataLayer: unknown[];
}
}
export {};
declare global працює лише у файлі-модулі (з import/export). export {} наприкінці перетворює файл на модуль, якщо інших імпортів немає.
У файлі-скрипті (без імпортів) глобальні оголошення пишуть без обгортки:
// globals.d.ts
interface Window {
dataLayer: unknown[];
}
declare const __APP_VERSION__: string; // значення, підставлене збирачем (define у Vite)
Змінні середовища Vite:
// src/vite-env.d.ts
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_APP_NAME: string;
readonly VITE_API_URL: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
Тепер import.meta.env.VITE_API_URL має тип рядка, а друкарська помилка в назві - помилку компіляції.
process.env у Node.js - через простір імен NodeJS:
declare global {
namespace NodeJS {
interface ProcessEnv {
DATABASE_URL: string;
NODE_ENV: 'development' | 'production' | 'test';
}
}
}
Пастки:
- оголошення - не гарантія. TypeScript повірить, що
window.App.userіснує, навіть якщо сервер його не передав. Для даних ззовні краще перевірка під час виконання (схема Zod) і чесні типи з| undefined; - змінні середовища оголошені як
string, але під час виконання можуть бути відсутні - валідація конфігурації при старті надійніша за самі типи; - файл не в
include- найчастіша причина, чому оголошення «не бачить» компілятор; varуdeclare globalдодає властивість і вglobalThis, аlet/const- ні; дляglobalThis.xпотрібен самеvar.
Докладніше в документації: Злиття оголошень: глобальне доповнення