# TypeScript 7.1 Beta: import attributes нарешті потрапили в ambient modules

> TypeScript 7.1 Beta додає підтримку import attributes в ambient module declarations і натякає на source phase imports. Що саме змінилось і чи варто вже пробувати.

- Автор: Юра Скиба (https://cookiesoftware.io)
- Опубліковано: 2026-11-01
- Категорія: JavaScript і TypeScript
- Canonical: https://cookiesoftware.io/blog/typescript-7-1-beta-import-attributes

---
TypeScript 7.1 вийшов у бета-версії 20 жовтня 2026 року, і головна фіча релізу – не продуктивність, а точніша типізація import attributes в ambient module declarations. Якщо коротко: тепер можна написати `declare module "*" with { type: "css" } { ... }` і компілятор нарешті розрізнятиме, який саме атрибут стоїть за імпортом файлу, а не просто дивитиметься на розширення. Встановити бету можна командою `npm install -D typescript@beta`.

Звучить як дрібниця, але якщо ти хоч раз імпортував CSS, JSON чи бінарні файли через бандлер з явним `with { type: ... }` – ти знаєш, наскільки криво це працювало раніше.

## Чому ambient-декларації були неповноцінними

Import attributes – це метадані після ключового слова `with`, які кажуть рантайму чи бандлеру, як інтерпретувати імпорт:

```ts
import myData from "./data/someData.json" with { type: "json" };
```

Крім `type: "json"`, екосистема рухається в бік інших атрибутів: ECMAScript-proposal для `type: "text"` повертає вміст файлу як рядок, proposal для `type: "bytes"` – як `Uint8Array`, а браузери вже кілька років підтримують `type: "css"` для імпорту файлу одразу як об’єкта `CSSStyleSheet`.

Щоб типізувати такі імпорти, розробники писали wildcard ambient-декларації:

```ts
declare module "*.css" {
  const content: CSSStyleSheet;
  export default content;
}

declare module "*.md" {
  const content: string;
  export default content;
}
```

Проблема в тому, що ця декларація прив’язана лише до розширення файлу і взагалі не бачить сам атрибут `with { type: ... }`. Звідси три конкретні болі:

1. Якщо хтось забув дописати `with { type: "text" }`, TypeScript все одно підставить тип зі старої декларації – помилку ти побачиш тільки в рантаймі.
2. Доводиться дублювати однакову декларацію для кожного розширення, навіть якщо вони обробляються однаково (`.txt` і `.md` обидва повертають `type: "text"`).
3. Найгірше – бандлер і браузер можуть по-різному інтерпретувати той самий файл залежно від наявності атрибута. Наприклад, бандлер може за замовчуванням застосовувати CSS Modules до `.css`-файлу, а явний `type: "css"` у імпорті – перевизначити цю поведінку і повернути сирий `CSSStyleSheet`. Стара wildcard-декларація просто не могла відрізнити ці два випадки.

## Що змінює TypeScript 7.1

Тепер ambient module declarations можна писати з прив’язкою до конкретного import attribute:

```ts
declare module "*" with { type: "css" } {
  const content: CSSStyleSheet;
  export default content;
}

declare module "*" with { type: "text" } {
  const content: string;
  export default content;
}

declare module "*" with { type: "bytes" } {
  const content: Uint8Array;
  export default content;
}
```

Тепер, якщо в проєкті з’явиться `import readme from "../README.md";` без атрибута – ти отримаєш помилку компіляції, а не сюрприз під час збірки. А якщо додати `with { type: "text" }`, TypeScript правильно виведе тип `string`.

Приклад з практики по типу «до / після»:

```ts
// ❌ до 7.1: компілюється, але в рантаймі впаде,
// бо реальний рантайм очікує type-атрибут
import readme from "../README.md";

// ✅ після 7.1: без атрибута – помилка типізації,
// з атрибутом – компілятор знає точний тип
import readme from "../README.md" with { type: "text" };
```

## Чесні обмеження нової фічі

Перш ніж переписувати всі свої `.d.ts`-файли, варто знати три речі.

По-перше, імпорти, які підпадають під такі декларації, **не звіряються з реальними файлами на диску**. TypeScript довіряє wildcard-патерну, а не перевіряє, чи файл фізично існує – ця перевірка все одно лягає на рантайм або білд-процес бандлера. Тобто типобезпека тут про форму даних, а не про існування файлу.

По-друге, ці декларації **не входять у built-in бібліотеки TypeScript**. Команда прямо радить бібліотекам не публікувати такі декларації у своїх npm-пакетах – кінцевий користувач має оголосити їх сам, у своєму проєкті. Причина практична: різні проєкти по-різному налаштовують глобальну область видимості (хтось підключає `lib.dom.d.ts`, хтось `@types/node`), і якщо бібліотека нав’яже власну декларацію `*.css`, це може конфліктувати з тим, що вже є в проєкті.

По-третє, фіча корисна рівно настільки, наскільки твій рантайм/бандлер взагалі підтримує import attributes. Якщо твій стек ще не оперує `with { type: ... }` – нові ambient-декларації просто не застосовні, і це нормально.

## Коли це варто брати

Якщо ти працюєш з інструментами, що вже підтримують import attributes – типізуй кожен `type` окремою декларацією замість wildcard по розширенню. Це найбільш відчутно для команд, які одночасно тягнуть CSS Modules через бандлер і браузерний `type: "css"` в одному кодбейсі – саме тут раніше губилася межа між двома різними типами.

Якщо ж твій проєкт імпортує нетипізовані файли рідко і без атрибутів – чекати на фінальний реліз і не поспішати з бетою цілком розумно.

Міні-чекліст перед тим як чіпати свої `.d.ts`:

- Чи бандлер або рантайм у проєкті вже підтримує синтаксис `with { type: ... }`?
- Чи є у проєкті місця, де один і той самий тип файлу імпортується з різними атрибутами (наприклад CSS Modules за замовчуванням і явний `type: "css"`)?
- Чи публікуєш ти бібліотеку назовні – і якщо так, чи варто прибрати власні ambient-декларації з пакета, залишивши їх документацією для користувачів?

Окремо варто тримати в полі зору другий анонс з цього релізу – source phase imports, новий ECMAScript-proposal з ключовим словом `import source` для default-імпортів. Джерело поки описує лише синтаксис (`import source SomeModule from "some-module";`), а детальну семантику – що саме повертає такий імпорт – команда TypeScript обіцяє розкрити окремо. Поки конкретики мало, і додавати її з голови не варто – почекаємо на повніший опис.

Якщо тобі цікаво, як узагалі влаштований сучасний TypeScript-компілятор і чому команда взялася за нього так активно переписувати, варто глянути [TypeScript 7.0: навіщо компілятор переписали на Go і що зміниться у твоєму проєкті](/blog/typescript-7-go-compiler) – 7.1 логічно продовжує той самий цикл релізів.

Практичний крок на сьогодні простий: постав бету в окремій гілці, перенеси свої wildcard-декларації на варіант з `with { type: ... }` і подивись, скільки забутих атрибутів компілятор знайде в існуючому коді. Швидше за все – хоч одне місце знайдеться.
