
Research
2025 Report: Destructive Malware in Open Source Packages
Destructive malware is rising across open source registries, using delays and kill switches to wipe code, break builds, and disrupt CI/CD.
@hubspot/ts-export-types
Advanced tools
Analyze public export types to extract simplified API types a TypeScript packages
Analyze exports for TypeScript packages
Produces a simplified type representation of all exported types, functions, classes, and other API surface elements.
Overview • Installation • Quick Start • Configuration • Output Format • API Reference
package.json exports field to identify public API boundariesnpm install @hubspot/ts-export-types
# 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
Create a ts-export-types.config.ts file in your project root:
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;
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
| 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 |
The exportFilter option allows you to selectively include exports:
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:
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
}
JSON format (outputFormat: 'json'):
{
"exports": { ... },
"types": { ... }
}
TypeScript module format (outputFormat: 'ts-module'):
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).
The analyzer produces a JSON structure with two main sections:
exports: A map of export paths to arrays of ExportNode objectstypes: A map of canonical type IDs to their resolved ApiNode definitionsInput:
export const sayHello = (name: string): string => `Hello, ${name}!`;
Output:
{
"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": {}
}
Input:
export interface UserProfile {
id: number;
name: string;
email?: string;
}
Output:
{
"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" }
}
]
}
}
}
Input:
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)
{
"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"
}
]
}
}
]
}
}
Input:
export type Box<T> = { value: T };
export type StringBox = Box<string>;
export type Usage = { stringBox: Box<string> };
Output: (showing type deduplication via typeId references)
{
"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" }
}
]
}
}
}
Input:
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)
{
"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"
}
}
}
]
}
}
}
| Type | Support |
|---|---|
Primitives (string, number, boolean, etc.) | ✅ |
| Literal types | ✅ |
| Union types | ✅ |
| Intersection types | ✅ |
| Object types / Interfaces | ✅ |
| 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 | ✅ |
analyzePackage(config: LoadedTsExportTypesConfig): AnalyzePackageResultMain analysis function. Takes a loaded configuration and returns the analysis result.
loadConfig(options: LoadConfigOptions): LoadedTsExportTypesConfigLoads 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.
Import from @hubspot/ts-export-types/analyze-result:
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'
);
Import from @hubspot/ts-export-types/analyze-result:
import {
isArrayNode,
isClassNode,
isFunctionNode,
isObjectNode,
isPrimitiveNode,
isTypeReferenceNode,
isUnionNode,
// ... and more
} from '@hubspot/ts-export-types/analyze-result';
if (isUnionNode(node)) {
node.types.forEach(processType);
}
All node types are exported from the main entry point:
import type {
ApiNode,
ApiNodeKind,
ArrayNode,
ClassNode,
ExportNode,
FunctionNode,
ObjectNode,
PropertyNode,
TypeReferenceNode,
UnionNode,
// ... and more
} from '@hubspot/ts-export-types';
Contributions are welcome! Please see our Contributing Guide for details on development setup and how to submit pull requests.
MIT © HubSpot
Built with ❤️ by the HubSpot team
FAQs
Analyze public export types to extract simplified API types a TypeScript packages
The npm package @hubspot/ts-export-types receives a total of 7 weekly downloads. As such, @hubspot/ts-export-types popularity was classified as not popular.
We found that @hubspot/ts-export-types demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 39 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
Destructive malware is rising across open source registries, using delays and kill switches to wipe code, break builds, and disrupt CI/CD.

Security News
Socket CTO Ahmad Nassri shares practical AI coding techniques, tools, and team workflows, plus what still feels noisy and why shipping remains human-led.

Research
/Security News
A five-month operation turned 27 npm packages into durable hosting for browser-run lures that mimic document-sharing portals and Microsoft sign-in, targeting 25 organizations across manufacturing, industrial automation, plastics, and healthcare for credential theft.