npm package: @hubspot/ts-export-types
Description: Analyze public export types to extract simplified API types a TypeScript packages
Latest version: 1.1.0

<p align="center">
  <img src="https://img.shields.io/npm/v/@hubspot/ts-export-types?color=%23f5c518&style=flat-square" alt="npm version" />
  <img src="https://img.shields.io/npm/l/@hubspot/ts-export-types?color=%2300d9ff&style=flat-square" alt="license" />
  <img src="https://img.shields.io/badge/TypeScript-5.9+-3178c6?style=flat-square&logo=typescript&logoColor=white" alt="TypeScript" />
  <img src="https://img.shields.io/badge/Node.js-18+-339933?style=flat-square&logo=node.js&logoColor=white" alt="Node.js" />
</p>

<h1 align="center">@hubspot/ts-export-types</h1>

<p align="center">
  <strong>Analyze exports for TypeScript packages</strong><br />
  Produces a simplified type representation of all exported types, functions, classes, and other API surface elements.
</p>

<p align="center">
  <a href="#overview">Overview</a> •
  <a href="#installation">Installation</a> •
  <a href="#quick-start">Quick Start</a> •
  <a href="#configuration">Configuration</a> •
  <a href="#output-format">Output Format</a> •
  <a href="#api-reference">API Reference</a>
</p>

---

## Overview

### Features

- 🔍 **TypeScript Compiler API** — Uses the official TypeScript compiler for accurate type analysis
- 📦 **Package-aware** — Respects `package.json` exports field to identify public API boundaries
- 🧬 **Complete type resolution** — Handles generics, unions, intersections, mapped types, conditional types, and more
- 🔄 **Deduplication** — Automatically deduplicates shared and recursive types
- 🌐 **External type tracking** — Identifies types from external packages vs. internal types
- 📝 **JSDoc preservation** — Captures documentation comments from your source code
- 🛠️ **CLI and programmatic API** — Use from the command line or integrate into your tooling

### Use cases

- 📚 **API Documentation** — Generate machine-readable API documentation for your TypeScript packages
- 🔄 **Breaking Change Detection** — Compare API snapshots to detect breaking changes between versions
- 🏗️ **Code Generation** — Use the type information to generate clients, validators, or other code
- 🧰 **IDE Tooling** — Build custom IDE integrations that understand your package's API
- 📊 **API Analytics** — Track how your API surface changes over time

## Installation

When using `npm`:

```bash
npm install @hubspot/ts-export-types
```

## Quick start

### CLI usage

```bash
# Generate API types with auto-discovered config
npx ts-export-types analyze

# Generate with a specific config file
npx ts-export-types analyze --config ./ts-export-types.config.ts
```

### Configuration file

Create a `ts-export-types.config.ts` file in your project root:

```ts
import type { TsExportTypesConfig } from '@hubspot/ts-export-types';

export default {
  analyze: {
    packageDirectory: '.',
    outputFile: './api/api.json',
    outputFormat: 'json',
    exportFilter: (exportInfo) => !exportInfo.exportName.startsWith('_'),
  },
} satisfies TsExportTypesConfig;
```

### Programmatic usage

```ts
import { analyzePackage, loadConfig } from '@hubspot/ts-export-types';

const config = loadConfig({
  configDirectory: '/path/to/my-package',
  userConfig: {
    analyze: {
      outputFile: null, // Don't write to file
    },
  },
});

const result = analyzePackage(config);

console.log(result.exports); // Map of export paths to ExportNode[]
console.log(result.types); // Map of type IDs to their resolved ApiNode
```

## Configuration

### Configuration options

| Option             | Type                                  | Default             | Description                                                |
| ------------------ | ------------------------------------- | ------------------- | ---------------------------------------------------------- |
| `packageDirectory` | `string`                              | Current directory   | Root directory of the package to analyze                   |
| `outputFile`       | `string \| null`                      | `./api/api.json`    | Output file path (`null` to disable writing)               |
| `outputFormat`     | `'json' \| 'ts-module'`               | `'ts-module'`       | Output format                                              |
| `outputExportName` | `string`                              | `'typesReader'`     | Name for the exported reader (only for `ts-module` format) |
| `exports`          | `PackageExports`                      | From `package.json` | Explicit exports map (overrides `package.json`)            |
| `exportFilter`     | `(exportInfo: ExportInfo) => boolean` | Include all         | Function to filter which exports to include                |
| `inlineTypes`      | `boolean`                             | `false`             | Inline non-recursive type references                       |

### Export filtering

The `exportFilter` option allows you to selectively include exports:

```ts
export default {
  analyze: {
    exportFilter: (exportInfo) => {
      // Exclude private exports (starting with _)
      if (exportInfo.exportName.startsWith('_')) return false;

      // Exclude internal paths
      if (exportInfo.filePath.includes('/internal/')) return false;

      return true;
    },
  },
} satisfies TsExportTypesConfig;
```

The `ExportInfo` object contains:

```ts
interface ExportInfo {
  filePath: string; // Full path to the source file
  exportName: string; // Name of the export
  isType?: boolean; // True if type-only export
  isDefault?: boolean; // True if default export
}
```

### Output formats

**JSON format** (`outputFormat: 'json'`):

```json
{
  "exports": { ... },
  "types": { ... }
}
```

**TypeScript module format** (`outputFormat: 'ts-module'`):

```ts
import { createAnalyzeResultReader } from '@hubspot/ts-export-types/analyze-result';

export * from "@hubspot/ts-export-types/analyze-result";

export const typesReader = createAnalyzeResultReader({ ... });
```

The TypeScript module format is useful for programmatically working with the generated types and provides helper functions for looking up exports and types. Use the `outputExportName` option to customize the export name (defaults to `typesReader`).

## Output format

The analyzer produces a JSON structure with two main sections:

- **`exports`**: A map of export paths to arrays of `ExportNode` objects
- **`types`**: A map of canonical type IDs to their resolved `ApiNode` definitions

### Example: Function export

**Input:**

```ts
export const sayHello = (name: string): string => `Hello, ${name}!`;
```

**Output:**

```json
{
  "exports": {
    ".": [
      {
        "kind": "export",
        "exportName": "sayHello",
        "declarationKind": "const",
        "type": {
          "kind": "function",
          "functionKind": "arrow",
          "parameters": [
            {
              "kind": "parameter",
              "name": "name",
              "type": { "kind": "primitive", "name": "string" }
            }
          ],
          "returnType": { "kind": "primitive", "name": "string" }
        }
      }
    ]
  },
  "types": {}
}
```

### Example: Interface export

**Input:**

```ts
export interface UserProfile {
  id: number;
  name: string;
  email?: string;
}
```

**Output:**

```json
{
  "exports": {
    ".": [
      {
        "kind": "export",
        "exportName": "UserProfile",
        "declarationKind": "interface",
        "isType": true,
        "type": {
          "kind": "type-reference",
          "typeId": "UserProfile:my-package:dist/index.d.ts",
          "typeString": "UserProfile"
        }
      }
    ]
  },
  "types": {
    "UserProfile:my-package:dist/index.d.ts": {
      "kind": "object",
      "properties": [
        {
          "kind": "property",
          "name": "id",
          "type": { "kind": "primitive", "name": "number" }
        },
        {
          "kind": "property",
          "name": "name",
          "type": { "kind": "primitive", "name": "string" }
        },
        {
          "kind": "property",
          "name": "email",
          "isOptional": true,
          "type": { "kind": "primitive", "name": "string" }
        }
      ]
    }
  }
}
```

### Example: Class with inheritance

**Input:**

```ts
interface Runnable {
  run(): void;
}

class BaseClass {
  public baseProp: string = '';
  public baseMethod(): void {}
}

export class DerivedClass extends BaseClass implements Runnable {
  derivedProp: number = 0;
  run(): void {}
}
```

**Output:** (properties and methods from inherited classes are collapsed)

```json
{
  "exports": {
    ".": [
      {
        "kind": "export",
        "exportName": "DerivedClass",
        "declarationKind": "class",
        "isType": true,
        "type": {
          "kind": "class",
          "name": "DerivedClass",
          "isAbstract": false,
          "constructors": [
            {
              "kind": "constructor",
              "parameters": [],
              "accessModifier": "public"
            }
          ],
          "properties": [
            {
              "kind": "property",
              "name": "derivedProp",
              "type": { "kind": "primitive", "name": "number" }
            },
            {
              "kind": "property",
              "name": "baseProp",
              "type": { "kind": "primitive", "name": "string" }
            }
          ],
          "methods": [
            {
              "kind": "method",
              "name": "run",
              "parameters": [],
              "returnType": { "kind": "primitive", "name": "void" },
              "isStatic": false,
              "isAbstract": false,
              "accessModifier": "public"
            },
            {
              "kind": "method",
              "name": "baseMethod",
              "parameters": [],
              "returnType": { "kind": "primitive", "name": "void" },
              "isStatic": false,
              "isAbstract": false,
              "accessModifier": "public"
            }
          ],
          "staticProperties": [],
          "staticMethods": [],
          "implements": [
            {
              "kind": "type-reference",
              "typeId": "Runnable:my-package:dist/index.d.ts",
              "typeString": "Runnable"
            }
          ]
        }
      }
    ]
  }
}
```

### Example: Generic types with shared references

**Input:**

```ts
export type Box<T> = { value: T };
export type StringBox = Box<string>;
export type Usage = { stringBox: Box<string> };
```

**Output:** (showing type deduplication via `typeId` references)

```json
{
  "exports": {
    ".": [
      {
        "kind": "export",
        "exportName": "Box",
        "declarationKind": "type",
        "isType": true,
        "type": {
          "kind": "type-reference",
          "typeId": "Box:my-package:dist/index.d.ts",
          "typeString": "Box<T>",
          "typeArguments": [{ "kind": "type-parameter", "name": "T" }]
        }
      }
    ]
  },
  "types": {
    "Box:my-package:dist/index.d.ts": {
      "kind": "object",
      "properties": [
        {
          "kind": "property",
          "name": "value",
          "type": { "kind": "type-parameter", "name": "T" }
        }
      ]
    }
  }
}
```

### Example: Recursive types

**Input:**

```ts
type SimpleOption = { type: 'simple'; value: string };
type OptionsGroup = { type: 'group'; options: Options[] };
type Options = SimpleOption | OptionsGroup;

export interface ComponentProps {
  options: Options;
}
```

**Output:** (recursive types handled via type references)

```json
{
  "types": {
    "Options:my-package:dist/index.d.ts": {
      "kind": "union",
      "types": [
        {
          "kind": "type-reference",
          "typeId": "SimpleOption:my-package:dist/index.d.ts",
          "typeString": "SimpleOption"
        },
        {
          "kind": "type-reference",
          "typeId": "OptionsGroup:my-package:dist/index.d.ts",
          "typeString": "OptionsGroup"
        }
      ]
    },
    "OptionsGroup:my-package:dist/index.d.ts": {
      "kind": "object",
      "properties": [
        {
          "kind": "property",
          "name": "type",
          "type": { "kind": "literal", "value": "group" }
        },
        {
          "kind": "property",
          "name": "options",
          "type": {
            "kind": "array",
            "elementType": {
              "kind": "type-reference",
              "typeId": "Options:my-package:dist/index.d.ts",
              "typeString": "Options"
            }
          }
        }
      ]
    }
  }
}
```

## Supported type constructs

| Type                                             | Support |
| ------------------------------------------------ | :-----: |
| Primitives (`string`, `number`, `boolean`, etc.) |   ✅    |
| Literal types                                    |   ✅    |
| Union types                                      |   ✅    |
| Intersection types                               |   ✅    |
| Object types / Interfaces                        |   ✅    |
| Index signatures                                 |   ✅    |
| Function types                                   |   ✅    |
| Array types                                      |   ✅    |
| Tuple types                                      |   ✅    |
| Generic types                                    |   ✅    |
| Type parameters                                  |   ✅    |
| Mapped types                                     |   ✅    |
| Conditional types                                |   ✅    |
| Type guards                                      |   ✅    |
| Built-in types (`Record`, `Map`, `Set`, etc.)    |   ✅    |
| External type references                         |   ✅    |
| Recursive types                                  |   ✅    |
| Classes (with inheritance)                       |   ✅    |
| Abstract classes                                 |   ✅    |
| Enums                                            |   ✅    |

## API reference

### Exported functions

#### `analyzePackage(config: LoadedTsExportTypesConfig): AnalyzePackageResult`

Main analysis function. Takes a loaded configuration and returns the analysis result.

#### `loadConfig(options: LoadConfigOptions): LoadedTsExportTypesConfig`

Loads and resolves configuration from options.

#### `loadConfigFromFile(configPath: string): Promise<LoadedTsExportTypesConfig>`

Loads configuration from a file path.

#### `loadConfigFromDirectory(directory: string): Promise<LoadedTsExportTypesConfig>`

Auto-discovers and loads configuration from a directory.

### Analyze result helpers

Import from `@hubspot/ts-export-types/analyze-result`:

```ts
import { createAnalyzeResultReader } from '@hubspot/ts-export-types/analyze-result';

const result = createAnalyzeResultReader(analyzeResult);

// Find an export by name
const exportNode = result.findExportByName({
  exportPath: '.',
  exportName: 'MyFunction',
});

// Find a referenced type by ID
const typeNode = result.findReferencedTypeById(
  'MyType:my-package:dist/index.d.ts'
);
```

### Type guards

Import from `@hubspot/ts-export-types/analyze-result`:

```ts
import {
  isArrayNode,
  isClassNode,
  isFunctionNode,
  isObjectNode,
  isPrimitiveNode,
  isTypeReferenceNode,
  isUnionNode,
  // ... and more
} from '@hubspot/ts-export-types/analyze-result';

if (isUnionNode(node)) {
  node.types.forEach(processType);
}
```

### ApiNode types

All node types are exported from the main entry point:

```ts
import type {
  ApiNode,
  ApiNodeKind,
  ArrayNode,
  ClassNode,
  ExportNode,
  FunctionNode,
  ObjectNode,
  PropertyNode,
  TypeReferenceNode,
  UnionNode,
  // ... and more
} from '@hubspot/ts-export-types';
```

## Requirements

- Node.js >= 18.0.0
- TypeScript >= 5.0.0

## Contributing

Contributions are welcome! Please see our [Contributing Guide](https://github.com/HubSpot/ts-export-types/blob/main/CONTRIBUTING.md) for details on development setup and how to submit pull requests.

## License

MIT © [HubSpot](https://github.com/HubSpot)

---

<p align="center">
  <sub>Built with ❤️ by the HubSpot team</sub>
</p>