Поле types в package.json является одним из ключевых
элементов для библиотек, написанных на TypeScript или предоставляющих
TypeScript-декларации. При сборке через Rollup корректная настройка
этого поля напрямую влияет на то, как TypeScript-потребители будут
видеть API библиотеки, как будет происходить автодополнение, проверка
типов и интеграция в IDE.
Поле types (или его исторический псевдоним
typings) указывает на главный файл деклараций TypeScript,
который описывает публичный интерфейс пакета. Этот файл обычно имеет
расширение .d.ts.
{
"name": "my-library",
"version": "1.0.0",
"types": "dist/index.d.ts"
}
Фактически это точка входа для системы типов TypeScript. Когда другой проект импортирует библиотеку, TypeScript использует именно этот файл для построения типов.
TypeScript при разрешении импортов проходит несколько шагов:
types или typings.d.ts рядом с main или
moduleindex.d.tsНаличие корректного types значительно ускоряет и
упрощает поиск типов, особенно в монорепозиториях и сложных структурах
пакетов.
Rollup сам по себе не генерирует TypeScript декларации, но он часто используется совместно с плагинами, которые решают эту задачу:
rollup-plugin-typescript2@rollup/plugin-typescripttsc как отдельный этап сборкиdts-bundle-generatorТипичный процесс выглядит так:
.ts в .js.d.tsЧаще всего итоговая структура пакета выглядит следующим образом:
dist/
index.js
index.esm.js
index.cjs.js
index.d.ts
Или более разветвлённый вариант:
dist/
esm/
index.js
cjs/
index.js
types/
index.d.ts
В обоих случаях поле types должно указывать на единый
входной файл деклараций.
types с
main, module и exportsВ современных пакетах используется комбинация нескольких полей:
{
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/types/index.d.ts"
}
Однако с появлением exports логика усложнилась:
{
"exports": {
".": {
"types": "./dist/types/index.d.ts",
"import": "./dist/esm/index.js",
"require": "./dist/cjs/index.js"
}
}
}
TypeScript начиная с версии 4.7 научился читать поле
types внутри exports. Это означает, что для
каждой точки входа можно определить собственные декларации.
Наиболее стабильный подход:
{
"compilerOptions": {
"declaration": true,
"emitDeclarationOnly": true,
"outDir": "dist/types"
}
}
После выполнения:
tsc -p tsconfig.json
Rollup используется только для JS.
Этот плагин позволяет генерировать декларации в рамках Rollup:
import typescript from "rollup-plugin-typescript2";
export default {
input: "src/index.ts",
output: [
{ file: "dist/index.esm.js", format: "esm" },
{ file: "dist/index.cjs.js", format: "cjs" }
],
plugins: [
typescript({
useTsconfigDeclarationDir: true
})
]
};
При этом types указывает на итоговый .d.ts
файл.
В больших библиотеках часто возникает проблема: TypeScript генерирует
множество .d.ts файлов, соответствующих структуре
исходников. Однако потребителю удобнее один файл.
Для этого применяются инструменты:
rollup-plugin-dtsdts-bundle-generatorПример Rollup-конфигурации для деклараций:
import dts from "rollup-plugin-dts";
export default {
input: "dist/types/index.d.ts",
output: {
file: "dist/index.d.ts",
format: "es"
},
plugins: [dts()]
};
В этом случае поле types становится:
{
"types": "dist/index.d.ts"
}
typesЧастая ошибка — указание пути, который не совпадает с фактической структурой:
{
"types": "dist/types/index.d.ts"
}
но файл фактически лежит в:
dist/index.d.ts
Если declaration не включён в TypeScript, Rollup не
сможет компенсировать это:
{
"compilerOptions": {
"declaration": false
}
}
Результат — отсутствие типизации у потребителей.
exports и
typesЕсли используется exports, но не указаны
types внутри него, TypeScript может не найти
декларации:
{
"exports": {
".": {
"import": "./dist/index.js"
}
}
}
Современные библиотеки часто имеют несколько входных точек:
src/
index.ts
utils.ts
math/index.ts
Соответствующий package.json:
{
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./utils": {
"types": "./dist/utils.d.ts",
"import": "./dist/utils.js"
}
}
}
Rollup в таком случае обычно конфигурируется через несколько входов:
export default {
input: {
index: "src/index.ts",
utils: "src/utils.ts"
}
};
И генерация деклараций должна повторять эту структуру.
Для библиотек, собираемых через Rollup, устойчивой считается схема:
.d.ts параллельно с
.jstypes указывает на финальный публичный файлsrc/
dist/
esm/
cjs/
types/
index.d.ts
{
"types": "dist/types/index.d.ts",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js"
}
IDE (VS Code, WebStorm) используют поле types как
первичный источник информации. Это влияет на:
Ошибочная конфигурация приводит к тому, что библиотека формально работает, но теряет всю ценность TypeScript-интеграции.
Rollup не вмешивается в .d.ts, но его стратегия
бандлинга влияет на то, как удобно организовать декларации:
.d.tsВ результате многие проекты переходят к стратегии:
Такое разделение минимизирует конфликты и делает поле
types предсказуемым и стабильным.