> For the complete documentation index, see [llms.txt](https://gotts.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://gotts.gitbook.io/docs/monorepo-infrastructure/monorepo/03-typescript.md).

# TypeScript

> **Part of**: [Monorepo Infrastructure PRD](/docs/monorepo-infrastructure/monorepo.md) | **Last Updated**: 2026-02-19

***

## Overview

TypeScript 5.8+ with **`bundler`** module resolution for all packages — applications, internal packages, and published libraries alike. Extension-free imports throughout; tsup handles dual ESM+CJS output at build time, and the `exports` field in each published `package.json` controls how consumers resolve entry points.

All configs extend from `@gotts.ai/typescript-config`, the shared config package.

## Shared Config Package

### `packages/typescript-config/package.json` (normative)

```jsonc
{
  "name": "@gotts.ai/typescript-config",
  "version": "0.0.0",
  "private": true,
  "license": "MIT",
  "exports": {
    "./base.json": "./base.json",
    "./library.json": "./library.json",
    "./nextjs.json": "./nextjs.json",
  },
}
```

> **No build step.** This package exports raw `.json` files. Consumers reference them via `extends` in their `tsconfig.json`.

### `base.json` (normative)

The foundation config. All other configs extend this.

```jsonc
{
  "$schema": "https://json.schemastore.org/tsconfig",
  "compilerOptions": {
    // Type checking
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noEmit": true,

    // Module system
    "module": "esnext",
    "moduleResolution": "bundler",
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "isolatedModules": true,

    // Emit
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,

    // Environment
    "target": "es2022",
    "lib": ["es2022"],

    // Misc
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "verbatimModuleSyntax": true,
    "erasableSyntaxOnly": true,
  },
  "exclude": ["node_modules", "dist"],
}
```

### `library.json` (normative)

For published packages (`@gotts.ai/safe`, `@gotts.ai/vault`, `@gotts.ai/agent-proxy`).

```jsonc
{
  "$schema": "https://json.schemastore.org/tsconfig",
  "extends": "./base.json",
  "compilerOptions": {
    "module": "esnext",
    "moduleResolution": "bundler",
    "declaration": true,
    "declarationMap": true,
    "outDir": "dist",
  },
}
```

### `nextjs.json` (normative)

For Next.js applications.

```jsonc
{
  "$schema": "https://json.schemastore.org/tsconfig",
  "extends": "./base.json",
  "compilerOptions": {
    "target": "es2022",
    "lib": ["dom", "dom.iterable", "es2022"],
    "module": "esnext",
    "moduleResolution": "bundler",
    "jsx": "preserve",
    "plugins": [{ "name": "next" }],
    "allowJs": true,
  },
}
```

## Consumer Configs

### Published package `tsconfig.json`

Example for `packages/safe/`:

```jsonc
{
  "extends": "@gotts.ai/typescript-config/library.json",
  "compilerOptions": {
    "outDir": "dist",
    "rootDir": "src",
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist", "test"],
}
```

### Internal package `tsconfig.json`

Example for `packages/ui/`:

```jsonc
{
  "extends": "@gotts.ai/typescript-config/base.json",
  "compilerOptions": {
    "jsx": "react-jsx",
  },
  "include": ["src"],
  "exclude": ["node_modules"],
}
```

### Next.js app `tsconfig.json`

Example for `apps/web/`:

```jsonc
{
  "extends": "@gotts.ai/typescript-config/nextjs.json",
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"],
    },
  },
  "include": ["src", "next-env.d.ts", ".next/types/**/*.ts"],
  "exclude": ["node_modules"],
}
```

### Root `tsconfig.json`

```jsonc
{
  "extends": "@gotts.ai/typescript-config/base.json",
  "compilerOptions": {
    "noEmit": true,
  },
  "include": [],
  "references": [
    { "path": "packages/safe" },
    { "path": "packages/vault" },
    { "path": "packages/agent-proxy" },
    { "path": "packages/ui" },
    { "path": "apps/web" },
  ],
}
```

## Key Decisions

### `erasableSyntaxOnly` (TS 5.8+)

Node.js 22+ supports `--experimental-strip-types`, which strips TypeScript syntax at startup without a transpiler. However, it only works with syntax that can be erased without affecting runtime behavior.

`erasableSyntaxOnly: true` enforces this constraint at the compiler level:

* **Allowed**: Type annotations, interfaces, type aliases, `as` casts
* **Forbidden**: `enum` (use `as const` objects), `namespace`, constructor parameter properties

This ensures all TypeScript files can run directly via `node --experimental-strip-types` or `tsx` without any build step during development.

### `verbatimModuleSyntax`

Replaces the deprecated `importsNotUsedAsValues` and `preserveValueImports` flags. Enforces that:

* Type-only imports use `import type { Foo }` syntax
* Type-only exports use `export type { Foo }` syntax
* No import elision ambiguity

This is required for correctness with `isolatedModules` and bundler-based workflows.

### `bundler` for All Packages

All packages — published libraries, internal packages, and applications — use `module: "esnext"` + `moduleResolution: "bundler"`:

1. tsup (or tsdown) handles module resolution and produces dual ESM+CJS output at build time
2. The `exports` field in each published `package.json` controls how consumers resolve entry points — this is what guarantees compatibility with Node.js, Bun, Deno, and bundlers
3. Extension-free imports are ergonomic and consistent across the entire monorepo
4. `nodenext` was originally used for libraries but added friction (mandatory `.js` extensions) with no benefit since a bundler produces the final output

### tsx for Development

[tsx](https://github.com/privatenumber/tsx) is used as the TypeScript runner for development scripts and dev servers:

```bash
# Run a script
pnpm tsx scripts/seed.ts

# Dev server with watch mode
pnpm tsx watch src/index.ts
```

tsx is preferred over `ts-node` because:

* Zero configuration required
* Faster startup (uses esbuild under the hood)
* Supports ESM natively
* Works with `paths` aliases
