TypeScript Code Generation

Generate type-safe TypeScript code with full type definitions and modern async/await patterns.

Overview

Circuitry generates TypeScript code that:

  • Includes comprehensive type definitions
  • Uses modern ES6+ features with types
  • Provides interface definitions for data flow
  • Supports strict mode compilation
  • Works with popular TypeScript frameworks

Generated Code Structure

import { httpRequest, callAI } from './circuitry-helpers.js';

interface WorkflowData {
  [key: string]: any;
  confirmed?: boolean;
  status?: string;
}

async function workflowName(input: WorkflowData = {}): Promise<WorkflowData> {
  let data: WorkflowData = input;

  try {
    // Each node transforms data with type safety
    data = await httpRequest('https://api.example.com', {
      method: 'GET'
    });

    // AI nodes become callAI calls
    const summary = await callAI('Summarize: ' + JSON.stringify(data), {
      model: 'gpt-4o',
      maxTokens: 500
    });
    data = { ...data, summary };

    return data;
  } catch (error) {
    console.error('Workflow error:', error);
    throw error;
  }
}

The helper files (circuitry-helpers.js and circuitry-ui.js) download alongside your generated workflow, together with matching type definition files (circuitry-helpers.d.ts and circuitry-ui.d.ts). Keep all of them in the same folder as the generated file and import with the relative ./circuitry-helpers.js specifier — TypeScript picks up the .d.ts definitions automatically.

Type Definitions

Included Type Definitions

# Downloaded with the helper files from the Code Generation dialog:
# - circuitry-helpers.d.ts  (types for httpRequest, callAI, ...)
# - circuitry-ui.d.ts       (types for confirmAction, getFormInput, ...)
# Keep them next to the .js files — no extra install step needed.

Core Types

// WorkflowData - Main data flow type
type WorkflowData = Record<string, any>;

// HTTP request options
interface HttpRequestOptions {
  method?: string;
  headers?: Record<string, string>;
  body?: any;
  data?: any;       // alias for body
  timeout?: number;
}

// AI call options
interface CallAIOptions {
  model?: string;        // e.g. 'gpt-4o' or 'claude-sonnet-4-5'
  systemPrompt?: string;
  temperature?: number;
  maxTokens?: number;
  apiKey?: string;       // falls back to OPENAI_API_KEY / ANTHROPIC_API_KEY
  endpoint?: string;     // custom OpenAI-compatible endpoint
}

// Confirmation Result
interface ConfirmResult {
  confirmed: boolean;
  timestamp: string;
  context?: any;
}

// Form Field
interface FormField {
  name: string;
  label?: string;
  type?: 'text' | 'number' | 'email' | 'checkbox' | 'select';
  required?: boolean;
  defaultValue?: any;
  options?: Array<{ value: string; label: string }>;
}

Helper Functions with Types

HTTP Requests

import { httpRequest } from './circuitry-helpers.js';

const response = await httpRequest(
  'https://api.example.com/users',
  {
    method: 'POST',
    headers: { 'Authorization': 'Bearer token' },
    body: { name: 'John', email: 'john@example.com' }
  }
);
// JSON responses are parsed automatically; other responses return text

AI Calls

import { callAI } from './circuitry-helpers.js';

// Routes to the right provider based on the model name
const answer: string = await callAI('Classify this ticket: ...', {
  model: 'claude-sonnet-4-5',
  systemPrompt: 'You are a support triage assistant.',
  maxTokens: 1024
});
// API keys are read from OPENAI_API_KEY / ANTHROPIC_API_KEY
// when not passed explicitly via options.apiKey

Templates and Embedded Code

import { replaceTemplateVariables, executeJavaScript } from './circuitry-helpers.js';

// Resolve {{input}}, {{previousOutput}} and dotted paths like {{input.user.name}}
const text = replaceTemplateVariables('Hello {{input.user.name}}', data);

// Run a code node's JavaScript with the current data (requires Node.js installed)
const result = executeJavaScript('return data.items.length', data);

User Interactions

import { confirmAction, getFormInput } from './circuitry-ui.js';

// Confirmation with typed result
const confirm: ConfirmResult = await confirmAction('Continue?');
if (confirm.confirmed) {
  // Type-safe access
}

// Form input with typed fields
const fields: FormField[] = [
  { name: 'email', type: 'email', required: true },
  { name: 'age', type: 'number' }
];
const formData = await getFormInput(fields);

Configuration

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "lib": ["ES2020", "DOM"],
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "outDir": "./dist"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

Running TypeScript Workflows

Direct Execution with tsx

# Install tsx globally
npm install -g tsx

# Run TypeScript directly
tsx workflow.ts

Compile and Run

# Compile to JavaScript
tsc workflow.ts

# Run compiled JavaScript
node workflow.js

With Node.js and ts-node

# Install dependencies
npm install -D typescript ts-node @types/node

# Run with ts-node
npx ts-node workflow.ts

Framework Integration

Next.js

// app/api/workflow/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { myWorkflow } from '@/lib/workflow';

export async function POST(request: NextRequest) {
  try {
    const input = await request.json();
    const result = await myWorkflow(input);
    return NextResponse.json(result);
  } catch (error) {
    return NextResponse.json(
      { error: error.message },
      { status: 500 }
    );
  }
}

Express with TypeScript

import express, { Request, Response } from 'express';
import { myWorkflow } from './workflow';

const app = express();
app.use(express.json());

app.post('/workflow', async (req: Request, res: Response) => {
  try {
    const result = await myWorkflow(req.body);
    res.json(result);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.listen(3000);

Advanced Types

Custom Workflow Types

// Define specific types for your workflow
interface UserData extends WorkflowData {
  userId: string;
  email: string;
  status: 'active' | 'inactive' | 'pending';
  metadata?: {
    lastLogin: Date;
    preferences: Record<string, any>;
  };
}

async function userWorkflow(input: Partial<UserData> = {}): Promise<UserData> {
  let data: UserData = {
    userId: '',
    email: '',
    status: 'pending',
    ...input
  };
  
  // Type-safe operations
  if (data.status === 'active') {
    // TypeScript knows status is 'active' here
  }
  
  return data;
}

Generic Workflow Functions

// Create reusable typed workflow functions
async function processWorkflow<T extends WorkflowData>(
  input: T,
  processor: (data: T) => Promise<T>
): Promise<T> {
  try {
    const processed = await processor(input);
    return processed;
  } catch (error) {
    console.error('Processing failed:', error);
    throw error;
  }
}

// Usage
const result = await processWorkflow(
  { userId: '123' },
  async (data) => {
    // Process data
    return { ...data, processed: true };
  }
);

Type Guards

// Type guard functions for runtime validation
function isConfirmResult(data: any): data is ConfirmResult {
  return data && 
    typeof data.confirmed === 'boolean' &&
    typeof data.timestamp === 'string';
}

// Usage in workflow
const result = await someOperation();
if (isConfirmResult(result)) {
  // TypeScript knows result is ConfirmResult here
  if (result.confirmed) {
    // Continue processing
  }
}

Error Handling with Types

// Custom error types
class WorkflowError extends Error {
  constructor(
    message: string,
    public code: string,
    public details?: any
  ) {
    super(message);
    this.name = 'WorkflowError';
  }
}

// Typed error handling
async function safeWorkflow(input: WorkflowData): Promise<WorkflowData> {
  try {
    return await myWorkflow(input);
  } catch (error) {
    if (error instanceof WorkflowError) {
      console.error(`Workflow error ${error.code}: ${error.message}`);
      // Handle specific workflow errors
    } else if (error instanceof Error) {
      console.error('General error:', error.message);
    } else {
      console.error('Unknown error:', error);
    }
    throw error;
  }
}

Testing with TypeScript

// workflow.test.ts
import { describe, it, expect } from '@jest/globals';
import { myWorkflow } from './workflow';

describe('Workflow Tests', () => {
  it('should process valid input', async () => {
    const input: WorkflowData = {
      userId: '123',
      action: 'process'
    };
    
    const result = await myWorkflow(input);
    
    expect(result).toHaveProperty('status');
    expect(result.status).toBe('success');
  });
  
  it('should handle errors gracefully', async () => {
    const input: WorkflowData = {
      invalid: true
    };
    
    await expect(myWorkflow(input)).rejects.toThrow();
  });
});

Best Practices

  1. Strict Mode: Always enable strict mode in tsconfig.json
  2. Type Everything: Avoid using 'any' type when possible
  3. Interfaces: Define interfaces for complex data structures
  4. Type Guards: Use type guards for runtime validation
  5. Error Types: Create custom error classes for better error handling