Cómo crear una librería npm tipada en TypeScript para desarrolladores
Cómo hacer una librería npm tipada con TypeScript
Tiempo estimado de lectura: 5 min
- Entrega dual: código ejecutable + declaraciones de tipo (.d.ts) para buena DX.
- Configuración esencial: tsconfig con declaration: true y salida limpia en dist/.
- Empaquetado: mapear main/module/types en package.json y validar con npm pack y tsc en proyecto consumidor.
- Exportaciones claras: centralizar API en src/index.ts y usar export type para tipos.
- Validación: pruebas de tipos en CI con tsd y tests locales antes de publish.
Introducción
Saber cómo hacer una librería npm tipada con TypeScript significa entregar dos cosas a la vez: código que la máquina ejecuta y tipos que el desarrollador consume. Desde el primer archivo hasta el .d.ts final, la meta es que quien instale tu paquete tenga autocompletado y verificaciones de tipo sin tocar su configuración. Esta guía va directo al flujo de producción: configuración, estructura, packaging y validación local.
Resumen rápido (lectores con prisa)
Una librería npm tipada combina JavaScript/TypeScript compilado y declaraciones de tipo (.d.ts). Usa tsc con declaration: true para generar tipos; centraliza la API en src/index.ts; apunta types en package.json al .d.ts maestro; valida con npm pack y ejecuta tsc --noEmit en un proyecto consumidor.
Cómo hacer una librería npm tipada con TypeScript — pasos y criterios
1. Estructura mínima
src/— código TypeScript.dist/— artefactos compilados (generado).package.json,tsconfig.json,README.md.
2. Instala y prepara TypeScript
npm init -y
npm install typescript --save-dev
npx tsc --init
3. tsconfig.json recomendable (base)
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"moduleResolution": "node",
"declaration": true,
"declarationMap": true,
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.test.ts"]
}
- declaration: true es obligatorio: genera
.d.ts. - declarationMap: true mejora la experiencia “Go to definition” en IDEs.
- outDir y rootDir mantienen el árbol limpio.
Documentación oficial TypeScript: TypeScript declaration files publishing
4. Código: exportaciones claras y barrel file
Centraliza la API pública en src/index.ts. Exporta valores y tipos explícitamente:
// src/types.ts
export interface LibraryConfig { timeout: number; }
// src/core.ts
import type { LibraryConfig } from './types';
export function init(cfg: LibraryConfig) { /* ... */ }
// src/index.ts
export { init } from './core';
export type { LibraryConfig } from './types';
Usa export type para separar contratos estáticos (no existen en runtime). Evita filtrar internals por accidente.
5. package.json: el contrato que vincula .js y .d.ts
{
"name": "mi-libreria-tipada",
"version": "1.0.0",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/index.d.ts",
"files": ["dist"],
"scripts": {
"build": "npm run build:cjs && npm run build:esm",
"build:cjs": "tsc --outDir dist/cjs --module commonjs",
"build:esm": "tsc --outDir dist/esm --module ESNext",
"prepublishOnly": "npm run build"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"require": "./dist/cjs/index.js",
"import": "./dist/esm/index.js"
}
}
}
- types debe apuntar al
.d.tsmaestro. - files actúa como whitelist; evita subir
srco configs. - exports (condicional) habilita resolución moderna y puede mapear CJS/ESM. Coloca
typesjunto a la condición raíz para que TypeScript lo resuelva bien.
Si publicas scoped package (p. ej. @scope/name), recuerda npm publish --access public para paquetes públicos.
6. ¿Generar .d.ts con tsc o con bundler?
- Para proyectos simples o librerías de funciones: usa
tsccondeclaration: true. Es robusto y sencillo. - Para bundles complejos (single-file output), usa un plugin para generar tipos como
rollup-plugin-dtso la opcióntsup --dts. Si eliges esta vía, verifica que el.d.tsfinal coincida con la estructura exportada.
Herramientas útiles:
- tsup (rápido)
- rollup + plugin dts
7. Validación local antes de publicar
- npm pack — crea un
.tgz. Ábrelo y verifica contenido:dist/,package.json,README.md.Docs: npm pack docs
- Instalación local en proyecto cliente:
cd ../project-test npm install /ruta/a/mi-libreria-tipada-1.0.0.tgzImporta y ejecuta
tsc --noEmiten el proyecto consumidor: sitypesestá mal, fallará aquí. - Tests de tipos automáticos: integra
tsden tu CI para asegurar que la API expuesta no cambia rompeints:
8. Dependencias y peerDependencies
- Declara en
peerDependencieslibrerías que el consumidor debe proveer (React, por ejemplo) para evitar duplicados. - Pon utilidades que la librería necesita en
dependencies.
9. Checklist rápido antes de publish
- [ ]
npm run buildgeneradistcon.jsy.d.ts. - [ ]
package.jsonapunta amain,module(si aplica) ytypes. - [ ]
filesincluye solodist,README.md,LICENSE. - [ ]
npm packinspeccionado. - [ ] Prueba de instalación local y
tsc --noEmiten proyecto consumidor. - [ ] Tests de tipos (
tsd) pasados en CI.
10. Comandos finales para publicar
npm login
npm publish --access public
(Usa --access public para paquetes scope públicos.)
Conclusión
Construir una librería tipada no es mágico: es disciplina. Configura tsc para emitir declaraciones, expone solo lo necesario y valida el paquete en un entorno consumidor antes de pulsar publish. El tiempo que inviertes en estos pasos se recupera con menos issues en integraciones y una mejor experiencia para quienes usan tu paquete.
FAQ
¿Por qué es obligatorio generar .d.ts?
Las declaraciones (.d.ts) proporcionan tipos a los consumidores de tu librería sin necesidad de que compilen tu código TypeScript. Sin ellas, los usuarios perderían autocompletado y las comprobaciones de tipo.
¿Puedo usar un bundler para generar tipos?
Sí. Para bundles single-file es común usar plugins como rollup-plugin-dts o herramientas como tsup --dts. Verifica que el resultado refleje la API exportada.
¿Qué debe apuntar el campo types en package.json?
types debe apuntar al .d.ts maestro que describe la API pública, por ejemplo dist/index.d.ts.
¿Cómo pruebo la experiencia del consumidor antes de publicar?
Usa npm pack para generar un .tgz, instálalo en un proyecto de prueba con npm install /ruta/mi-libreria.tgz y ejecuta tsc --noEmit en el proyecto consumidor.
¿Debo usar peerDependencies para React?
Sí. Declara frameworks como React en peerDependencies para evitar múltiples copias y problemas de compatibilidad en el proyecto consumidor.
¿Qué herramientas recomiendo para pruebas de tipos en CI?
Integra tsd en tu CI para pruebas automáticas de tipos. Revisa tsd para más detalles.
¿Qué archivos incluir en el paquete publicado?
Usa files en package.json para incluir solo dist, README.md y LICENSE. Evita subir src o configuraciones internas.
