Доповнення модуля (module augmentation) додає поля до інтерфейсів, оголошених у чужому пакеті, не змінюючи сам пакет. Працює через злиття інтерфейсів.
Приклад з Vue - глобальна властивість у шаблонах:
// src/types/vue.d.ts
import type { Translator } from '../i18n';
declare module 'vue' {
interface ComponentCustomProperties {
$t: Translator;
}
}
Тепер $t має тип у всіх шаблонах і this в Options API.
Приклад з Pinia - власна опція стора, з Vue Router - типізоване meta:
import 'vue-router';
declare module 'vue-router' {
interface RouteMeta {
requiresAuth?: boolean;
title?: string;
}
}
Express - поле в запиті. Типи Express оголошені в глобальному просторі імен, тому доповнюють його через declare global:
import type { User } from './models.js';
declare global {
namespace Express {
interface Request {
user?: User;
}
}
}
Обов'язкові умови:
- файл має бути модулем - містити хоча б один
importчиexportна верхньому рівні (часто додаютьexport {}). У файлі-скриптіdeclare module 'vue'замінить оголошення модуля замість доповнення - і всі типи Vue зникнуть. Це дзеркальна протилежність ситуації зdeclare module '*.svg', якому, навпаки, потрібен скрипт; - назва модуля має точно збігатися з тим, що імпортують (
'vue', а не'@vue/runtime-core', якщо бібліотека радить саме'vue'); - доповнювати можна лише існуючі інтерфейси - нові експорти чи нові модулі так не додаються;
- файл має потрапити в компіляцію (
includeуtsconfig).
Бібліотеки часто проєктують такі точки розширення навмисно: порожній інтерфейс-«реєстр», який користувач доповнює, - і всі API бібліотеки автоматично отримують точні типи (події, маршрути, теми, сховища).
Пастка: доповнення діють глобально для всієї програми. Поле, додане до Request у одному місці, видно всюди - тож тип має бути чесним (user?: User, а не user: User, якщо middleware автентифікації працює не на всіх маршрутах).
Докладніше в документації: Злиття оголошень: доповнення модуля