Пакет на TypeScript публікує скомпільований JavaScript і файли декларацій .d.ts (з declaration: true). Як їх знайде споживач - вирішує package.json.
Сучасний варіант - поле exports з умовою types:
{
"name": "@acme/money",
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"./format": {
"types": "./dist/format.d.ts",
"import": "./dist/format.js"
}
},
"files": ["dist"]
}
Правила, які найчастіше порушують:
types- першою умовою в кожному об'єкті. Умови перевіряються по порядку, і TypeScript візьме першу підходящу. Якщоimportстоїть раніше, компілятор може «знайти» JavaScript замість декларацій;- кожна точка входу в
exportsмає власніtypes- верхньорівневе полеtypesдля підшляхів не діє; - подвійний пакет (ESM + CommonJS) потребує окремих декларацій для кожного формату (
index.d.tsіindex.d.cts). Одні.d.tsна обидва формати TypeScript інтерпретує в одному режимі - і споживачі з іншим форматом отримують неправильні типи (класична проблема «masquerading as ESM/CJS»); filesмає включати каталог з.d.ts, інакше їх не буде в опублікованому архіві.
Старе поле types/typings у корені - для споживачів зі старими налаштуваннями резолюції. Опції moduleResolution: node10 у TypeScript 6/7 вже немає, тож сучасні споживачі читають exports; поле в корені можна лишити як запасний варіант.
Залежності типів: якщо публічні декларації посилаються на типи іншого пакета (import type { Request } from 'express'), той пакет (@types/express) має бути в dependencies чи peerDependencies, а не в devDependencies - інакше у споживачів типи зламаються.
Перевірка перед публікацією:
@arethetypeswrong/cli- перевіряє, що типи правильно резолвляться для всіх режимів (node16ESM/CJS,bundler);publint- узгодженістьexports,filesі реальних файлів;npm pack --dry-run- що саме потрапить в архів.
isolatedDeclarations спрощує генерацію декларацій сторонніми інструментами (і пришвидшує збирання), але вимагає явних типів на експортах.