Skip to content
Back to skills

Lang Typescript Library Dev

ASecurity

TypeScript-specific library/package development patterns. Use when creating npm packages, configuring package.json exports, setting up tsconfig.json for libraries, generating declaration files, publishing to npm, or configuring ESM/CJS dual packages. Extends meta-library-dev with TypeScript tooling and ecosystem patterns.

  • 8 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added June 2, 2026
developmentjavascripttypescriptgojavabashreactnodetestinggitapi

Works with

  • cli
  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned June 2, 2026

npx -y skills add aRustyDev/agents --skill lang-typescript-library-dev --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Lang Typescript Library Dev?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Lang Typescript Library Dev
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/arustydev-lang-typescript-library-dev/badge)](https://www.skillsdirectory.com/skills/arustydev-lang-typescript-library-dev)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: lang-typescript-library-dev
description: TypeScript-specific library/package development patterns. Use when creating npm packages, configuring package.json exports, setting up tsconfig.json for libraries, generating declaration files, publishing to npm, or configuring ESM/CJS dual packages. Extends meta-library-dev with TypeScript tooling and ecosystem patterns.
---

# TypeScript Library Development

TypeScript-specific patterns for library/package development. This skill extends `meta-library-dev` with TypeScript tooling, module system configuration, and npm ecosystem practices.

## This Skill Extends

- `meta-library-dev` - Foundational library patterns (API design, versioning, testing strategies)

For general concepts like semantic versioning, module organization principles, and testing pyramids, see the meta-skill first.

## This Skill Adds

- **TypeScript tooling**: tsconfig.json for libraries, declaration files, source maps
- **Package configuration**: package.json exports, ESM/CJS dual packages, bundling
- **npm ecosystem**: Publishing workflow, scoped packages, monorepos

## This Skill Does NOT Cover

- General library patterns - see `meta-library-dev`
- TypeScript syntax/patterns - see `lang-typescript-patterns-dev`
- React component libraries - see frontend skills
- Node.js application development

---

## Overview

Publishing a TypeScript library requires careful configuration of multiple interconnected systems:

```
┌─────────────────────────────────────────────────────────────────┐
│                    TypeScript Library Stack                     │
├─────────────────────────────────────────────────────────────────┤
│  Source Code (src/)                                             │
│       │                                                         │
│       ▼                                                         │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐         │
│  │ tsconfig    │───▶│ TypeScript  │───▶│ Declaration │         │
│  │   .json     │    │  Compiler   │    │ Files (.d.ts)│         │
│  └─────────────┘    └─────────────┘    └─────────────┘         │
│       │                   │                   │                 │
│       │                   ▼                   │                 │
│       │            ┌─────────────┐            │                 │
│       │            │  JavaScript │            │                 │
│       │            │   Output    │            │                 │
│       │            └─────────────┘            │                 │
│       │                   │                   │                 │
│       ▼                   ▼                   ▼                 │
│  ┌─────────────────────────────────────────────────────┐       │
│  │                   package.json                       │       │
│  │  ┌─────────┐  ┌─────────┐  ┌─────────┐             │       │
│  │  │ exports │  │  main   │  │  types  │             │       │
│  │  │  field  │  │ module  │  │  field  │             │       │
│  │  └─────────┘  └─────────┘  └─────────┘             │       │
│  └─────────────────────────────────────────────────────┘       │
│                          │                                      │
│                          ▼                                      │
│                    ┌───────────┐                                │
│                    │    npm    │                                │
│                    │  publish  │                                │
│                    └───────────┘                                │
└─────────────────────────────────────────────────────────────────┘
```

**Key Decision Points:**

| Decision | Options | Recommendation |
|----------|---------|----------------|
| Module format | ESM-only, CJS-only, Dual | ESM-only for new packages; Dual if supporting legacy |
| Build tool | tsc, tsup, unbuild, rollup | tsup for simplicity; tsc for control |
| Declaration files | Inline, Separate dir | Inline (same dir as JS) |
| Monorepo tool | pnpm workspaces, turborepo, nx | pnpm workspaces for simplicity |

---

## Quick Reference

| Task | Command |
|------|---------|
| New package | `npm init` or `pnpm init` |
| Build | `tsc` or bundler command |
| Test | `vitest` or `jest` |
| Lint | `eslint .` |
| Format | `prettier --write .` |
| Pack (dry run) | `npm pack --dry-run` |
| Publish | `npm publish` |
| Publish (scoped public) | `npm publish --access public` |

---

## Package.json Structure

### Required Fields for Publishing

```json
{
  "name": "my-library",
  "version": "1.0.0",
  "description": "A brief description of what this library does",
  "license": "MIT",
  "author": "Your Name <email@example.com>",
  "repository": {
    "type": "git",
    "url": "https://github.com/username/repo"
  },
  "keywords": ["keyword1", "keyword2", "keyword3"],
  "type": "module",
  "main": "./dist/index.cjs",
  "module": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js",
      "require": "./dist/index.cjs"
    }
  },
  "files": ["dist"],
  "engines": {
    "node": ">=18.0.0"
  }
}
```

### Exports Field (Modern)

The `exports` field controls what can be imported:

```json
{
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js",
      "require": "./dist/index.cjs"
    },
    "./utils": {
      "types": "./dist/utils.d.ts",
      "import": "./dist/utils.js",
      "require": "./dist/utils.cjs"
    },
    "./package.json": "./package.json"
  }
}
```

**Order matters**: `types` must come first for TypeScript resolution.

### Files Field

Control what gets published:

```json
{
  "files": [
    "dist",
    "!dist/**/*.test.*",
    "!dist/**/*.spec.*"
  ]
}
```

Always verify with `npm pack --dry-run`.

---

## tsconfig.json for Libraries

### Base Configuration

```json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "lib": ["ES2022"],

    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,

    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,

    "outDir": "./dist",
    "rootDir": "./src",

    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
}
```

### Declaration Files

| Option | Purpose |
|--------|---------|
| `declaration: true` | Generate `.d.ts` files |
| `declarationMap: true` | Enable "Go to Definition" in source |
| `emitDeclarationOnly: true` | Only emit declarations (use with bundler) |
| `declarationDir` | Separate output for declarations |

### Module Systems

| Config | Output | Use Case |
|--------|--------|----------|
| `"module": "NodeNext"` | ESM with `.js` | Modern Node.js packages |
| `"module": "CommonJS"` | CJS with `.js` | Legacy Node.js |
| `"module": "ESNext"` | ESM | For bundlers |

---

## ESM/CJS Dual Package

### Strategy 1: Dual Build (Recommended)

Build both formats from TypeScript:

```json
{
  "scripts": {
    "build": "npm run build:esm && npm run build:cjs",
    "build:esm": "tsc -p tsconfig.esm.json",
    "build:cjs": "tsc -p tsconfig.cjs.json"
  }
}
```

**tsconfig.esm.json:**
```json
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "module": "NodeNext",
    "outDir": "./dist/esm"
  }
}
```

**tsconfig.cjs.json:**
```json
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "module": "CommonJS",
    "outDir": "./dist/cjs"
  }
}
```

### Strategy 2: Use a Bundler

Use tsup, unbuild, or rollup for simpler dual builds:

**tsup.config.ts:**
```typescript
import { defineConfig } from 'tsup';

export default defineConfig({
  entry: ['src/index.ts'],
  format: ['esm', 'cjs'],
  dts: true,
  clean: true,
  sourcemap: true,
});
```

**package.json scripts:**
```json
{
  "scripts": {
    "build": "tsup"
  }
}
```

### Strategy 3: ESM-Only (Simplest)

For modern packages, consider ESM-only:

```json
{
  "type": "module",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "default": "./dist/index.js"
    }
  }
}
```

---

## Public API Design

### Export Patterns

**Explicit Named Exports (Preferred):**
```typescript
// src/index.ts
export { parse, serialize } from './parser.js';
export { validate } from './validator.js';
export type { Config, Options, Result } from './types.js';
```

**Avoid Default Exports:**
```typescript
// Avoid: Harder to tree-shake, inconsistent naming
export default class Parser { }

// Prefer: Named exports
export class Parser { }
```

### Type Exports

**Use `export type` for type-only exports:**
```typescript
// Enables proper tree-shaking and prevents runtime import
export type { User, Config } from './types.js';

// Re-export with types
export { parseUser, type ParseOptions } from './parser.js';
```

### Barrel Files

**src/index.ts (public API):**
```typescript
// Public API - explicit exports
export { createClient } from './client.js';
export { parse, serialize } from './parser.js';
export type { ClientOptions, ParseResult } from './types.js';

// Do NOT re-export internal modules
// import './internal.js';  // Wrong
```

---

## Type Declaration Best Practices

### Provide Good Types

```typescript
// Good: Specific, useful types
export interface ClientOptions {
  baseUrl: string;
  timeout?: number;
  headers?: Record<string, string>;
}

export function createClient(options: ClientOptions): Client;

// Avoid: Overly generic
export function createClient(options: object): unknown;
```

### Use Generics Appropriately

```typescript
// Good: Generic with constraints
export function parse<T extends Record<string, unknown>>(
  input: string,
  schema: Schema<T>
): T;

// Good: Infer return type
export function map<T, U>(
  items: T[],
  fn: (item: T) => U
): U[];
```

### Document with JSDoc

```typescript
/**
 * Parses a configuration string into a typed object.
 *
 * @param input - The configuration string to parse
 * @param options - Optional parsing options
 * @returns The parsed configuration object
 * @throws {ParseError} If the input is malformed
 *
 * @example
 * ```typescript
 * const config = parse('key=value', { strict: true });
 * console.log(config.key); // 'value'
 * ```
 */
export function parse<T>(input: string, options?: ParseOptions): T;
```

---

## Testing Libraries

### Vitest Configuration

**vitest.config.ts:**
```typescript
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    globals: true,
    environment: 'node',
    include: ['src/**/*.test.ts'],
    coverage: {
      provider: 'v8',
      reporter: ['text', 'json', 'html'],
      exclude: ['**/*.test.ts', '**/*.d.ts'],
    },
  },
});
```

### Test File Organization

```
src/
├── parser.ts
├── parser.test.ts      # Unit tests next to source
├── validator.ts
├── validator.test.ts
└── __tests__/          # Or separate test directory
    └── integration.test.ts
```

### Type Testing

**Test that types work correctly:**
```typescript
import { expectTypeOf } from 'vitest';
import { parse } from './parser.js';

test('parse returns correct type', () => {
  const result = parse('{"name": "test"}');
  expectTypeOf(result).toEqualTypeOf<ParsedResult>();
});
```

---

## Monorepo Patterns

### pnpm Workspace

**pnpm-workspace.yaml:**
```yaml
packages:
  - 'packages/*'
```

### Package Structure

```
my-monorepo/
├── package.json
├── pnpm-workspace.yaml
├── tsconfig.json          # Base config
└── packages/
    ├── core/
    │   ├── package.json
    │   ├── tsconfig.json  # Extends base
    │   └── src/
    └── utils/
        ├── package.json
        ├── tsconfig.json
        └── src/
```

### Internal Dependencies

```json
{
  "name": "@myorg/app",
  "dependencies": {
    "@myorg/core": "workspace:*",
    "@myorg/utils": "workspace:*"
  }
}
```

### Project References

**Root tsconfig.json:**
```json
{
  "references": [
    { "path": "./packages/core" },
    { "path": "./packages/utils" }
  ]
}
```

**Package tsconfig.json:**
```json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "composite": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "references": [
    { "path": "../utils" }
  ]
}
```

---

## Publishing to npm

### Pre-publish Checklist

- [ ] `npm run build` succeeds
- [ ] `npm run test` passes
- [ ] `npm run lint` passes
- [ ] Version bumped in package.json
- [ ] CHANGELOG.md updated
- [ ] README.md is current
- [ ] `npm pack --dry-run` shows correct files
- [ ] Types are correctly generated
- [ ] Exports work: `node -e "import('my-lib')"`

### Publishing Commands

```bash
# Verify package contents
npm pack --dry-run

# Publish to npm
npm publish

# Publish scoped package as public
npm publish --access public

# Publish with tag (for pre-releases)
npm publish --tag beta
```

### Scoped Packages

```json
{
  "name": "@myorg/my-library",
  "publishConfig": {
    "access": "public"
  }
}
```

### Automation with Changesets

```bash
# Initialize changesets
npx changeset init

# Add a changeset
npx changeset

# Version packages
npx changeset version

# Publish
npx changeset publish
```

---

## Common Dependencies

### Build Tools

```json
{
  "devDependencies": {
    "typescript": "^5.0.0",
    "tsup": "^8.0.0",
    "@types/node": "^20.0.0"
  }
}
```

### Testing

```json
{
  "devDependencies": {
    "vitest": "^1.0.0",
    "@vitest/coverage-v8": "^1.0.0"
  }
}
```

### Linting/Formatting

```json
{
  "devDependencies": {
    "eslint": "^8.0.0",
    "typescript-eslint": "^7.0.0",
    "prettier": "^3.0.0"
  }
}
```

---

## Anti-Patterns

### 1. Missing Types Field

```json
// Bad: Types not specified
{
  "main": "./dist/index.js"
}

// Good: Types explicitly declared
{
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts"
}
```

### 2. Wrong Export Order

```json
// Bad: types not first
{
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "types": "./dist/index.d.ts"
    }
  }
}

// Good: types first
{
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js"
    }
  }
}
```

### 3. Publishing Source Files

```json
// Bad: Publishing everything
{
  "files": ["src", "dist"]
}

// Good: Only publish dist
{
  "files": ["dist"]
}
```

### 4. Missing Peer Dependencies

```json
// Bad: Bundling React in a React library
{
  "dependencies": {
    "react": "^18.0.0"
  }
}

// Good: Peer dependency
{
  "peerDependencies": {
    "react": "^18.0.0"
  }
}
```

---

## Troubleshooting

### Types Not Found by Consumers

**Symptom:** `Cannot find module 'my-lib' or its corresponding type declarations`

**Causes & Fixes:**

| Cause | Fix |
|-------|-----|
| Missing `types` field | Add `"types": "./dist/index.d.ts"` to package.json |
| Wrong export order | Put `types` first in exports conditions |
| Declaration files not generated | Set `"declaration": true` in tsconfig.json |
| Files not published | Check `files` field includes `dist` |

**Diagnostic:**
```bash
# Check what's actually published
npm pack --dry-run

# Validate types configuration
npx @arethetypeswrong/cli my-package
```

### ESM/CJS Import Errors

**Symptom:** `ERR_REQUIRE_ESM` or `Must use import to load ES Module`

**Common Fixes:**

```json
// Ensure package.json has correct type
{
  "type": "module"  // For ESM-first packages
}

// Or provide both formats in exports
{
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js",
      "require": "./dist/index.cjs"
    }
  }
}
```

### Declaration Files Missing Exports

**Symptom:** Types exist but some exports show as `any`

**Fixes:**
1. Ensure all exports use `export` keyword (not just `module.exports`)
2. Check `include` in tsconfig.json covers all source files
3. Verify no `// @ts-ignore` hiding type errors

### Monorepo Package Resolution

**Symptom:** `Cannot find module '@myorg/shared'` in monorepo

**Fixes:**

```json
// tsconfig.json - Add path mapping
{
  "compilerOptions": {
    "paths": {
      "@myorg/*": ["./packages/*/src"]
    }
  }
}

// Or use TypeScript project references
{
  "references": [
    { "path": "../shared" }
  ]
}
```

### Build Output Issues

| Problem | Solution |
|---------|----------|
| Output files have wrong extension | Check `module` setting matches desired output |
| Source maps not working | Enable `sourceMap` and `declarationMap` |
| Test files in dist | Add test patterns to `exclude` in tsconfig |
| node_modules in output | Ensure `rootDir` is set to `./src` |

### Publishing Failures

**Pre-publish checklist:**
```bash
# 1. Verify package contents
npm pack --dry-run

# 2. Test local install
npm pack && npm install ./my-package-1.0.0.tgz

# 3. Test imports work
node -e "import('my-package').then(console.log)"

# 4. Check for accidental secrets
grep -r "api_key\|password\|secret" dist/
```

---

## References

- `meta-library-dev` - Foundational library patterns
- `lang-typescript-patterns-dev` - TypeScript syntax and patterns
- [TypeScript Handbook: Publishing](https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html)
- [npm Docs: package.json](https://docs.npmjs.com/cli/v10/configuring-npm/package-json)
- [Are The Types Wrong?](https://arethetypeswrong.github.io/) - Validate package types

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…