Короткий ответ: классы из TypeScript npm-пакета лучше экспортировать через единый src/index.ts, собрать пакет в dist, сгенерировать .d.ts-типы и явно указать входы в package.json. Тогда пользователь сможет импортировать класс обычным способом, а TypeScript увидит типы
Базовая структура:
my-package/
src/
UserService.ts
index.ts
dist/
package.json
tsconfig.json
Класс в отдельном файле
export class UserService {
getName(): string {
return "Dinar";
}
}
Здесь используется named export. Это удобный вариант для библиотек, потому что в одном пакете может быть несколько классов и функций
Единая точка входа
src/index.ts:
export { UserService } from "./UserService";
Теперь пользователь пакета не должен знать внутреннюю структуру:
import { UserService } from "my-package";
const service = new UserService();
console.log(service.getName());
Если потом вы перенесете файл внутри src, публичный импорт можно оставить прежним
tsconfig для сборки
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Node",
"declaration": true,
"outDir": "dist",
"rootDir": "src",
"strict": true
},
"include": ["src"]
}
Ключевой параметр — declaration: true. Он создает .d.ts-файлы, без которых TypeScript-пользователь пакета потеряет типы
package.json
Простой вариант:
{
"name": "my-package",
"version": "1.0.0",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": ["dist"],
"scripts": {
"build": "tsc"
}
}
Более явный вариант через exports:
{
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
}
}
exports помогает Node.js и сборщикам понять, какие входы пакета публичные. Не стоит заставлять пользователей импортировать из my-package/dist/UserService
Default export или named export
export default class UserService {}
Импорт:
import UserService from "my-package";
export class UserService {}
Импорт:
import { UserService } from "my-package";
Для библиотек чаще удобнее named export: он лучше масштабируется, понятнее при автодополнении и снижает риск путаницы с именами
Несколько классов
export { UserService } from "./UserService";
export { OrderService } from "./OrderService";
export { PaymentService } from "./PaymentService";
Использование:
import { UserService, OrderService } from "my-package";
Если классы связаны, можно экспортировать и типы:
export type { User, CreateUserInput } from "./types";
Проверка до публикации
Соберите пакет:
npm run build
Проверьте, что в dist есть:
index.js
index.d.ts
UserService.js
UserService.d.ts
Затем создайте маленький тестовый проект и установите пакет локально:
npm pack
npm install ../my-package/my-package-1.0.0.tgz
Так вы увидите реальные ошибки импорта до публикации в npm
Частые ошибки
Первая ошибка — экспортировать класс из файла, но забыть переэкспортировать его в index.ts
Вторая ошибка — не включить declaration: true
Третья ошибка — опубликовать src, но не опубликовать собранный dist
Четвертая ошибка — смешать CommonJS и ESM без ясной настройки package.json
Пятая ошибка — менять внутренние пути пакета, на которые уже начали ссылаться пользователи. Публичный API лучше держать через index.ts
Самопроверка
Создайте класс Calculator, экспортируйте его из src/index.ts, соберите пакет и импортируйте в отдельном проекте. Если редактор видит методы класса и TypeScript показывает подсказки, экспорт и .d.ts работают
Что почитать дальше по TypeScript
Если нужен общий маршрут по теме, откройте рубрику TypeScript. Для соседних задач пригодятся эти разборы:
- TypeScript npm: как установить, запустить и проверить
- Как экспортировать значение из замыкания в TypeScript
- 10 вопросов по TypeScript и ответы на них
- Any, unknown, never и strict