TypeScript Container Setups and Examples for Claude Code

Overview

Containerization is a critical practice for creating consistent, secure, and scalable environments for your Claude Code agents. This guide provides comprehensive information on TypeScript-specific container setups using Docker. By following these patterns, you can eliminate “it works on my machine” issues, streamline dependency management, and enforce security boundaries.

This setup is foundational for achieving the performance and reliability discussed in our other guides. Before diving in, consider reviewing these related documents:

Table of Contents

  1. TypeScript Docker Container Configuration
  2. Testing Frameworks in Containers
  3. Node.js Version Management
  4. Build Optimization
  5. Real-World Examples
  6. Development Workflow Best Practices

TypeScript Docker Container Configuration

1. tsconfig.json Best Practices

For containerized TypeScript projects, configure your tsconfig.json with these optimal settings:

{
  "compilerOptions": {
    "target": "ES2017",
    "module": "commonjs",
    "lib": ["es2017"],
    "outDir": "./dist",
    "rootDir": "./src",
    "baseUrl": "./src",
    "moduleResolution": "node",
    "noImplicitAny": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

2. Multi-Stage Docker Build

Multi-stage builds create optimized images with minimal size and dependencies:

# Build stage
FROM node:18-alpine AS build
WORKDIR /app
 
# Copy package files
COPY package*.json ./
COPY tsconfig.json ./
 
# Install all dependencies
RUN npm ci
 
# Copy source code
COPY src ./src
 
# Build TypeScript
RUN npm run build
 
# Production stage
FROM node:18-alpine AS production
WORKDIR /app
 
# Copy package files
COPY package*.json ./
 
# Install only production dependencies
RUN npm ci --only=production
 
# Copy built application from build stage
COPY --from=build /app/dist ./dist
 
# Set NODE_ENV
ENV NODE_ENV=production
 
# Start the application
CMD ["node", "dist/index.js"]

3. Development Container with Hot Reloading

For development environments, use this Docker Compose configuration:

version: '3.8'
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      target: development
    volumes:
      - ./src:/app/src
      - ./tsconfig.json:/app/tsconfig.json
      - ./nodemon.json:/app/nodemon.json
    ports:
      - "3000:3000"
      - "9229:9229" # Debug port
    environment:
      - NODE_ENV=development
    command: npm run dev

With corresponding nodemon.json:

{
  "watch": ["src"],
  "ext": "ts",
  "ignore": ["src/**/*.spec.ts"],
  "exec": "node --inspect=0.0.0.0:9229 -r ts-node/register src/index.ts"
}

4. Package.json Scripts

Configure your scripts for optimal development and production workflows:

{
  "scripts": {
    "dev": "nodemon",
    "build": "rimraf dist && tsc",
    "start": "node dist/index.js",
    "start:prod": "npm run build && npm start",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage",
    "lint": "eslint src/**/*.ts",
    "type-check": "tsc --noEmit"
  }
}

Testing Frameworks in Containers

1. Vitest Configuration

Vitest provides out-of-box TypeScript support with minimal configuration:

// vitest.config.ts
import { defineConfig } from 'vitest/config'
import path from 'path'
 
export default defineConfig({
  test: {
    globals: true,
    environment: 'node',
    coverage: {
      provider: 'v8',
      reporter: ['text', 'json', 'html']
    }
  },
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src')
    }
  }
})

Docker Compose for integration testing:

version: '3.9'
services:
  app:
    build: .
    depends_on:
      - db
    environment:
      - DATABASE_URL=postgresql://user:password@db:5432/testdb
    command: npm run test:integration
 
  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=testdb
    ports:
      - "5433:5432"

2. Jest Configuration

For Jest with TypeScript in containers:

// jest.config.js
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  roots: ['<rootDir>/src'],
  testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
  transform: {
    '^.+\\.ts$': 'ts-jest'
  },
  collectCoverageFrom: [
    'src/**/*.ts',
    '!src/**/*.d.ts',
    '!src/**/*.test.ts'
  ],
  coverageDirectory: 'coverage',
  coverageReporters: ['text', 'lcov', 'html']
}

Node.js Version Management

1. Claude Code Version Management Issues

Claude Code uses the system Node.js version by default. To ensure proper version management:

Using NVM

{
  "mcpServers": {
    "your-server": {
      "command": "bash",
      "args": [
        "-c",
        "source ~/.nvm/nvm.sh && nvm use && npx your-command"
      ]
    }
  }
}

Using Volta

# Install Volta in Docker
RUN curl https://get.volta.sh | bash
ENV VOLTA_HOME=/root/.volta
ENV PATH=$VOLTA_HOME/bin:$PATH
 
# Pin Node and npm versions
RUN volta install node@18.17.0
RUN volta install npm@9.8.1

2. Docker as Version Manager

Create a consistent Node.js environment using Docker:

FROM node:18.17.0-alpine AS base
WORKDIR /app
 
# Install global tools
RUN npm install -g typescript@5.2.2 \
    ts-node@10.9.1 \
    nodemon@3.0.1
 
# Set up non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001
 
# Switch to non-root user
USER nodejs

Build Optimization

1. esbuild Integration

For blazing fast builds, integrate esbuild with webpack:

// webpack.config.js
const { ESBuildMinifyPlugin } = require('esbuild-loader')
 
module.exports = {
  module: {
    rules: [
      {
        test: /\.[jt]sx?$/,
        loader: 'esbuild-loader',
        options: {
          target: 'es2015',
          tsconfig: './tsconfig.json'
        }
      }
    ]
  },
  optimization: {
    minimizer: [
      new ESBuildMinifyPlugin({
        target: 'es2015',
        css: true
      })
    ]
  }
}

2. Vite Configuration

For modern TypeScript projects, Vite provides excellent performance:

// vite.config.ts
import { defineConfig } from 'vite'
import { resolve } from 'path'
 
export default defineConfig({
  build: {
    target: 'esnext',
    lib: {
      entry: resolve(__dirname, 'src/index.ts'),
      name: 'MyLibrary',
      fileName: 'my-library'
    },
    rollupOptions: {
      external: ['node:*'],
      output: {
        globals: {
          node: 'node'
        }
      }
    }
  },
  resolve: {
    alias: {
      '@': resolve(__dirname, './src')
    }
  }
})

3. Docker Build Optimization

Optimize Docker builds for TypeScript:

# Use BuildKit for better caching
# syntax=docker/dockerfile:1
 
FROM node:18-alpine AS dependencies
WORKDIR /app
# Copy only package files for better layer caching
COPY package*.json ./
RUN npm ci
 
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
COPY --from=dependencies /app/node_modules ./node_modules
COPY tsconfig*.json ./
COPY src ./src
RUN npm run build
 
FROM node:18-alpine AS runtime
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY --from=build /app/dist ./dist
USER node
EXPOSE 3000
CMD ["node", "dist/index.js"]

Real-World Examples

1. Claude Code Hooks (by John Lindquist)

A TypeScript-based system for configuring Claude Code hooks:

// Example hook configuration
interface ClaudeHook {
  name: string
  trigger: 'pre-commit' | 'post-commit' | 'pre-push'
  script: string
  enabled: boolean
}
 
const hooks: ClaudeHook[] = [
  {
    name: 'type-check',
    trigger: 'pre-commit',
    script: 'npm run type-check',
    enabled: true
  },
  {
    name: 'test',
    trigger: 'pre-push',
    script: 'npm test',
    enabled: true
  }
]

2. Claude Code UI

A full-stack TypeScript application with React frontend:

// Example component structure
import React from 'react'
import { CodeMirror } from '@codemirror/view'
import { typescript } from '@codemirror/lang-typescript'
 
export const CodeEditor: React.FC = () => {
  return (
    <CodeMirror
      extensions={[typescript()]}
      theme="dark"
      value={code}
      onChange={handleChange}
    />
  )
}

3. Claude Code Templates CLI

A TypeScript CLI tool for managing Claude Code templates:

// Example CLI command
import { Command } from 'commander'
import { setupProject } from './setup'
 
const program = new Command()
 
program
  .command('init <framework>')
  .description('Initialize a new project with Claude Code')
  .option('-t, --typescript', 'Use TypeScript', true)
  .action(async (framework, options) => {
    await setupProject(framework, options)
  })
 
program.parse(process.argv)

Development Workflow Best Practices

1. Claude Code with Devcontainers

Create a .devcontainer/devcontainer.json:

{
  "name": "TypeScript Claude Code",
  "dockerFile": "Dockerfile",
  "customizations": {
    "vscode": {
      "extensions": [
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode",
        "ms-vscode.vscode-typescript-next"
      ],
      "settings": {
        "typescript.tsdk": "node_modules/typescript/lib",
        "editor.formatOnSave": true,
        "editor.defaultFormatter": "esbenp.prettier-vscode"
      }
    }
  },
  "postCreateCommand": "npm install",
  "forwardPorts": [3000, 9229],
  "remoteUser": "node"
}

2. MCP Server Integration

Configure Model Context Protocol for Claude:

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/app"]
    },
    "typescript": {
      "command": "npx",
      "args": ["-y", "typescript-language-server", "--stdio"]
    }
  }
}

3. Security Best Practices

  1. Container Isolation: Run Claude Code in isolated containers to prevent system-wide access
  2. Network Security: Implement firewall rules to restrict container network access
  3. Volume Mounts: Use named volumes instead of bind mounts for better security
  4. User Permissions: Run containers as non-root users
# Security-focused Dockerfile snippet
FROM node:18-alpine
# Create app directory
RUN mkdir -p /home/node/app && chown -R node:node /home/node/app
WORKDIR /home/node/app
# Switch to non-root user
USER node
# Copy files with correct ownership
COPY --chown=node:node package*.json ./
RUN npm ci --only=production
COPY --chown=node:node . .

4. Performance Monitoring

Add performance monitoring to your TypeScript containers:

// performance.ts
import { performance } from 'perf_hooks'
 
export class PerformanceMonitor {
  private marks: Map<string, number> = new Map()
 
  mark(name: string): void {
    this.marks.set(name, performance.now())
  }
 
  measure(name: string, startMark: string): number {
    const start = this.marks.get(startMark)
    if (!start) throw new Error(`Mark ${startMark} not found`)
    
    const duration = performance.now() - start
    console.log(`${name}: ${duration.toFixed(2)}ms`)
    return duration
  }
}

Conclusion

This guide provides a comprehensive overview of TypeScript-specific container setups for Claude Code. The key takeaways are:

  1. Use multi-stage Docker builds for optimal image sizes
  2. Configure TypeScript properly for containerized environments
  3. Leverage modern build tools like esbuild and Vite for performance
  4. Implement proper testing strategies with Jest or Vitest
  5. Handle Node.js version management carefully in containers
  6. Follow security best practices with container isolation
  7. Use devcontainers for consistent development environments

These practices enable efficient, secure, and scalable TypeScript development with Claude Code in containerized environments.