TypeScript Schema Generation

Automatic type-safe interfaces for your feature flags with complete client code

TypeScript Integration Overview

FlagFlow automatically generates complete TypeScript definitions for your feature flags, providing full type safety and IntelliSense support. The generated code includes type definitions, default values, hash constants, and ready-to-use client functions.

Zero Configuration: Simply download the generated TypeScript file and start using fully-typed feature flags in your application immediately.

What Gets Generated

When you access /type/typescript, FlagFlow generates a complete TypeScript file containing:

Type Definitions

  • Hierarchical TypeScript interfaces
  • Union types for enums
  • Array types for tags
  • Readonly properties for immutability

Runtime Objects

  • Default value objects
  • Hash constants for validation
  • Group descriptor mappings
  • Type-to-group associations

Client Functions

  • Basic fetch functions
  • Factory functions
  • Cached implementations
  • Error handling utilities

Complete Examples

  • Ready-to-use code snippets
  • Integration patterns
  • Best practices
  • Performance optimizations

Flag Type Mapping

FlagFlow maps its internal flag types to appropriate TypeScript types:

FlagFlow TypeTypeScript TypeExample
BOOLEANbooleantrue | false
INTEGERnumber42
STRINGstring"hello world"
ENUMUnion Type"red" | "green" | "blue"
TAGArray of Union("red" | "green" | "blue")[]
AB-TESTstring"A" | "B" (runtime)

Example Generated Types

Here's an example of the TypeScript types generated for a hierarchical flag structure:

Generated Type Definitions
// Generated TypeScript Definitions

// Root type for all flags
export type FlagFlow = {
  readonly enableNewFeature: boolean;
  readonly maxRetries: number;
  readonly theme: "light" | "dark" | "auto";
  readonly enabledFeatures: ("analytics" | "chat" | "notifications")[];
  readonly abTestVariant: string; // AB-TEST type
  readonly accounting: FlagFlow__Accounting;
};

// Nested group type
export type FlagFlow__Accounting = {
  readonly enableCurrencyExchange: boolean;
  readonly defaultCurrency: "USD" | "EUR" | "HUF";
  readonly huf: FlagFlow__Accounting__Huf;
};

// Deeply nested group type
export type FlagFlow__Accounting__Huf = {
  readonly exchangeRate: number;
  readonly allowExchange: boolean;
};

// Complete type mapping for all groups
export type FlagFlow_DescriptorTypeMap = {
  '#root': FlagFlow;
  'accounting': FlagFlow__Accounting;
  'accounting/huf': FlagFlow__Accounting__Huf;
};

Default Value Objects

Generated Default Values
// Generated default value objects for runtime use
export const defaultFlagFlow: FlagFlow = {
  enableNewFeature: false,
  maxRetries: 3,
  theme: "auto",
  enabledFeatures: ["analytics"],
  abTestVariant: "A", // Default for AB-TEST
  accounting: defaultFlagFlow__Accounting,
};

export const defaultFlagFlow__Accounting: FlagFlow__Accounting = {
  enableCurrencyExchange: true,
  defaultCurrency: "USD",
  huf: defaultFlagFlow__Accounting__Huf,
};

export const defaultFlagFlow__Accounting__Huf: FlagFlow__Accounting__Huf = {
  exchangeRate: 380.5,
  allowExchange: false,
};

Generated Client Code

The TypeScript file includes complete, ready-to-use client code with full type safety:

Generated Client Functions
import axios from "axios";

import { flagFlow_Descriptors, FlagFlow_DescriptorTypeMap } from "./flagflowTypes";

const FLAGFLOW_BASE_URL = 'http://localhost:3000/flags';

// Basic fetch function with full type safety
export const fetchData = async <K extends keyof FlagFlow_DescriptorTypeMap>(
    key: K
): Promise<FlagFlow_DescriptorTypeMap[K]> => {
    const { uri, hash } = flagFlow_Descriptors[key];
    const { data } = await axios.get<FlagFlow_DescriptorTypeMap[K]>(
        FLAGFLOW_BASE_URL + uri, {
        responseType: 'json',
        headers: {
            'Content-Type': 'application/json',
            'x-accept-flaggroup-hash': hash,
        }
    })
    return data
}

// Factory function for creating fetch functions
export const createFetchFunction = <K extends keyof FlagFlow_DescriptorTypeMap>(
    key: K
): (() => Promise<FlagFlow_DescriptorTypeMap[K]>) => () => fetchData(key);

// Cached fetch function with TTL support
export const createFetchFunctionWithCache = <K extends keyof FlagFlow_DescriptorTypeMap>(
    key: K,
    ttlSeconds: number = 60
) => {
    const fetchDataFn = createFetchFunction(key);
    let data: FlagFlow_DescriptorTypeMap[K] | undefined;
    let lastFetchTime: number = 0;
    return async (): Promise<FlagFlow_DescriptorTypeMap[K]> => {
        const now = Date.now();
        if (data && (now - lastFetchTime < ttlSeconds * 1000)) return data;

        data = await fetchDataFn();
        lastFetchTime = now;
        return data;
    }
}

Usage Examples

Basic Usage

Basic Flag Usage
import { fetchData, FlagFlow } from './flagflowTypes';

// Fetch all root flags with full type safety
const flags: FlagFlow = await fetchData('#root');

// Access flags with IntelliSense support
if (flags.enableNewFeature) {
  console.log('New feature is enabled!');
}

// Type-safe enum access
console.log(`Current theme: ${flags.theme}`); // "light" | "dark" | "auto"

// Array type support
if (flags.enabledFeatures.includes('analytics')) {
  initializeAnalytics();
}

// Nested group access
const accountingFlags = await fetchData('accounting');
console.log(`Exchange enabled: ${accountingFlags.enableCurrencyExchange}`);

With Caching

Cached Flag Usage
import { createFetchFunctionWithCache } from './flagflowTypes';

// Create cached fetch function (30 second TTL)
const getCachedFlags = createFetchFunctionWithCache('#root', 30);

// Use in your application
async function checkFeatures() {
  const flags = await getCachedFlags(); // Uses cache if fresh
  
  return {
    showNewUI: flags.enableNewFeature,
    theme: flags.theme,
    maxRetries: flags.maxRetries
  };
}

// Multiple calls within 30 seconds use cached data
const features1 = await checkFeatures(); // Network request
const features2 = await checkFeatures(); // From cache
const features3 = await checkFeatures(); // From cache

React Hook Example

React Hook Integration
import React, { useState, useEffect } from 'react';
import { createFetchFunctionWithCache, FlagFlow } from './flagflowTypes';

// Create cached fetcher outside component
const getCachedFlags = createFetchFunctionWithCache('#root', 60);

export function useFeatureFlags() {
  const [flags, setFlags] = useState<FlagFlow | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    let mounted = true;
    
    getCachedFlags()
      .then(data => {
        if (mounted) {
          setFlags(data);
          setLoading(false);
        }
      })
      .catch(err => {
        if (mounted) {
          setError(err);
          setLoading(false);
        }
      });

    return () => { mounted = false; };
  }, []);

  return { flags, loading, error };
}

// Usage in component
function MyComponent() {
  const { flags, loading, error } = useFeatureFlags();
  
  if (loading) return <div>Loading flags...</div>;
  if (error) return <div>Error: {error.message}</div>;
  if (!flags) return null;
  
  return (
    <div className={flags.theme}>
      {flags.enableNewFeature && <NewFeatureComponent />}
      <Analytics enabled={flags.enabledFeatures.includes('analytics')} />
    </div>
  );
}

Generation API

TypeScript Generation Endpoint

API Usage
# Get TypeScript definitions
curl http://localhost:3000/type/typescript

# Save to file
curl http://localhost:3000/type/typescript > flagflowTypes.ts

# Download as file attachment
curl http://localhost:3000/type/typescript?download=1

File Header Information

The generated file includes helpful metadata:

Generated File Header
// This file is auto-generated by FlagFlow
// Generated at: 2024-08-10T14:30:25.123Z
// FlagFlow Version: 1.0.0
// Total Flags: 15
// Total Groups: 4
// 
// To regenerate this file:
// curl http://localhost:3000/type/typescript > flagflowTypes.ts
//
// ⚠️  Do not edit this file manually - changes will be lost!

Integration Patterns

Application Initialization

Service Pattern
// flagService.ts
import { createFetchFunctionWithCache, FlagFlow } from './flagflowTypes';

class FlagService {
  private getCachedFlags = createFetchFunctionWithCache('#root', 300); // 5 minutes
  private flags: FlagFlow | null = null;
  
  async initialize(): Promise<void> {
    try {
      this.flags = await this.getCachedFlags();
      console.log('✅ Feature flags loaded successfully');
    } catch (error) {
      console.error('❌ Failed to load feature flags:', error);
      throw error;
    }
  }
  
  isEnabled(feature: keyof FlagFlow): boolean {
    return this.flags?.[feature] === true;
  }
  
  getValue<K extends keyof FlagFlow>(key: K): FlagFlow[K] | null {
    return this.flags?.[key] || null;
  }
  
  async refresh(): Promise<void> {
    // Force refresh by creating new fetcher
    this.getCachedFlags = createFetchFunctionWithCache('#root', 300);
    await this.initialize();
  }
}

export const flagService = new FlagService();

// Initialize at app startup
flagService.initialize().catch(console.error);

Environment-Specific Configuration

Environment Configuration
// config.ts
const FLAGFLOW_URLS = {
  development: 'http://localhost:3000',
  staging: 'https://staging-flags.example.com',
  production: 'https://flags.example.com'
};

const environment = process.env.NODE_ENV || 'development';
export const FLAGFLOW_BASE_URL = FLAGFLOW_URLS[environment] + '/flags';

// flagService.ts - Updated with environment config
import { FLAGFLOW_BASE_URL } from './config';

// Override the generated base URL
const originalFetchData = fetchData;
export const fetchData = async <K extends keyof FlagFlow_DescriptorTypeMap>(
  key: K
): Promise<FlagFlow_DescriptorTypeMap[K]> => {
  const { uri, hash } = flagFlow_Descriptors[key];
  const { data } = await axios.get<FlagFlow_DescriptorTypeMap[K]>(
    FLAGFLOW_BASE_URL + uri, {
    responseType: 'json',
    headers: {
      'Content-Type': 'application/json',
      'x-accept-flaggroup-hash': hash,
    }
  });
  return data;
};

Best Practices

Code Organization

  • Keep generated file separate - Don't modify the generated TypeScript file
  • Create wrapper services - Abstract flag access behind service interfaces
  • Use caching wisely - Balance freshness with performance needs
  • Handle errors gracefully - Provide fallback behavior when flags are unavailable

Type Safety Tips

  • Leverage IntelliSense - Use IDE autocompletion for flag names and values
  • Type guards - Create type guards for runtime flag validation
  • Const assertions - Use 'as const' for flag group keys
  • Generic constraints - Constrain generic types to valid flag groups

Performance Optimization

  • Group-specific fetching - Fetch only needed flag groups
  • Appropriate cache TTLs - Balance freshness with request frequency
  • Batch initialization - Load all required flags at application startup
  • Error boundary patterns - Isolate flag loading failures

Troubleshooting

Type Errors After Schema Changes

  • Regenerate TypeScript definitions: curl /type/typescript > flagflowTypes.ts
  • Clear TypeScript cache in your IDE
  • Restart TypeScript language server
  • Check for breaking changes in flag types

Runtime Type Mismatches

  • Verify hash validation is working (check 409 errors)
  • Ensure you're using the latest generated types
  • Check that default values match expected types
  • Validate enum values are still valid

Import/Module Issues

  • Ensure flagflowTypes.ts is in the correct location
  • Check TypeScript module resolution settings
  • Verify file paths in import statements
  • Check for circular dependency issues
© 2025 FlagFlow All Rights Reserved. llms.txt