TypeScript Support

Type definitions and typed usage

DBindly v2.3.0 includes TypeScript definitions for full type safety and IDE support.

Installation

The type definitions are included with the main package. Reference them in your project:

<script src="https://api.dbindly.com/dbindly.min.js"></script>
<script src="https://api.dbindly.com/dbindly.d.ts"></script>

Or download dbindly.d.ts for local use.

Type Definitions

DBindlyConfig

Configuration options for initialization:

interface DBindlyConfig {
  apiUrl: string;
  apiKey?: string;
  cache?: boolean;
  cacheTTL?: number;
  debug?: boolean;
  retryAttempts?: number;
  retryDelay?: number;
  timeout?: number;
  showSkeletons?: boolean;
  onError?: (error: Error) => void;
  onLoad?: (data: Record<string, unknown>) => void;
  onDataLoaded?: (data: Record<string, unknown>) => void;
}

DBindlyMetrics

Performance metrics returned by getMetrics():

interface DBindlyMetrics {
  requestCount: number;
  totalLoadTime: number;
  cacheHits: number;
  cacheMisses: number;
  errors: number;
  averageLoadTime: number;  // Calculated
  cacheHitRate: number;     // Calculated
}

CollectionOptions

Options for collection queries:

interface CollectionSort {
  field: string;
  direction: 'asc' | 'desc';
}
 
interface CollectionFilter {
  field: string;
  operator: 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'contains' | 'startsWith' | 'endsWith';
  value: unknown;
}
 
interface CollectionOptions {
  page?: number;
  limit?: number;
  fields?: string[];
  sort?: CollectionSort;
  filters?: CollectionFilter[];
  search?: string;
}

CollectionResponse

Response from collection operations:

interface CollectionResponse<T = Record<string, unknown>> {
  data: T[];
  total: number;
  page: number;
  limit: number;
  totalPages: number;
  hasMore: boolean;
}

Event Types

Custom event detail types:

interface ModelChangeEventDetail {
  field: string;
  value: unknown;
  element: HTMLElement;
}
 
interface ActionEventDetail {
  action: string;
  data: Record<string, unknown>;
  element: HTMLElement;
}
 
interface ErrorEventDetail {
  error: Error;
  field?: string;
}

Typed Usage Examples

Basic Initialization

// TypeScript knows all valid config options
DBindly.init({
  apiUrl: 'https://api.example.com',
  apiKey: 'pk_live_xxx',
  cache: true,
  cacheTTL: 300000,
  onError: (error: Error) => {
    console.error('DBindly error:', error.message);
  }
});

Loading Data

// Returns Promise<Record<string, unknown>>
const data = await DBindly.load('product-123');
 
// Type assertion for specific data shape
interface Product {
  id: string;
  name: string;
  price: number;
  category: string;
}
 
const product = await DBindly.load('product-123') as Product;
console.log(product.name); // TypeScript knows this exists

Typed Collections

interface BlogPost {
  id: string;
  title: string;
  excerpt: string;
  author: {
    name: string;
    avatar: string;
  };
  publishedAt: string;
  tags: string[];
}
 
// Generic type parameter for response data
const result = await DBindly.fetchCollection<BlogPost>('blog-posts', {
  page: 1,
  limit: 10,
  sort: { field: 'publishedAt', direction: 'desc' }
});
 
// result.data is typed as BlogPost[]
result.data.forEach(post => {
  console.log(post.title);        // TypeScript knows this exists
  console.log(post.author.name);  // Nested properties too
});

Event Listeners

// Typed event listener for model changes
document.addEventListener('dbindly:model-change', (e: CustomEvent<ModelChangeEventDetail>) => {
  console.log('Field:', e.detail.field);
  console.log('Value:', e.detail.value);
  console.log('Element:', e.detail.element);
});
 
// Typed action handler
document.addEventListener('dbindly:action', (e: CustomEvent<ActionEventDetail>) => {
  const { action, data, element } = e.detail;
 
  switch (action) {
    case 'submit-form':
      handleFormSubmit(data);
      break;
    case 'delete-item':
      handleDelete(element.dataset.id);
      break;
  }
});
 
// Typed error handler
document.addEventListener('dbindly:error', (e: CustomEvent<ErrorEventDetail>) => {
  const { error, field } = e.detail;
  if (field) {
    showFieldError(field, error.message);
  } else {
    showGlobalError(error.message);
  }
});

Form Data

interface ContactForm {
  name: string;
  email: string;
  message: string;
  subscribe: boolean;
}
 
// Type assertion for form data
const formData = DBindly.getFormData() as ContactForm;
 
// Now TypeScript knows the shape
console.log(formData.name);
console.log(formData.email);

Metrics

const metrics: DBindlyMetrics = DBindly.getMetrics();
 
console.log(`Requests: ${metrics.requestCount}`);
console.log(`Cache hit rate: ${(metrics.cacheHitRate * 100).toFixed(1)}%`);
console.log(`Average load time: ${metrics.averageLoadTime}ms`);

Global Declaration

DBindly is available globally:

// Access via window
window.DBindly.init({ apiUrl: '...' });
 
// Or directly (ambient declaration)
DBindly.init({ apiUrl: '...' });

The type definitions include global augmentation:

declare global {
  interface Window {
    DBindly: DBindly;
  }
 
  const DBindly: DBindly;
 
  interface DocumentEventMap {
    'dbindly:model-change': CustomEvent<ModelChangeEventDetail>;
    'dbindly:action': CustomEvent<ActionEventDetail>;
    'dbindly:error': CustomEvent<ErrorEventDetail>;
    'dbindly:loaded': CustomEvent<{ data: Record<string, unknown> }>;
    'dbindly:collection-loaded': CustomEvent<{ slug: string; data: CollectionResponse }>;
  }
}

Complete TypeScript Example

// types.ts
interface User {
  id: string;
  email: string;
  profile: {
    firstName: string;
    lastName: string;
    avatar?: string;
  };
  preferences: {
    newsletter: boolean;
    theme: 'light' | 'dark';
  };
}
 
// app.ts
import type { DBindlyConfig, DBindlyMetrics, CollectionResponse } from './dbindly';
 
const config: DBindlyConfig = {
  apiUrl: process.env.API_URL || 'https://api.example.com',
  apiKey: process.env.DBINDLY_KEY,
  cache: true,
  cacheTTL: 5 * 60 * 1000, // 5 minutes
  debug: process.env.NODE_ENV === 'development',
  onError: (error: Error) => {
    DBindly.showError(error.message);
    trackError(error);
  },
  onDataLoaded: (data: Record<string, unknown>) => {
    trackPageLoad(data);
  }
};
 
DBindly.init(config);
 
// Load user profile with type safety
async function loadUserProfile(userId: string): Promise<User | null> {
  try {
    const user = await DBindly.load(`user-${userId}`) as User;
    return user;
  } catch (error) {
    console.error('Failed to load user:', error);
    return null;
  }
}
 
// Handle form submission with typed data
document.addEventListener('dbindly:action', async (e: CustomEvent<ActionEventDetail>) => {
  if (e.detail.action === 'update-profile') {
    const formData = e.detail.data as Partial<User['profile']>;
 
    DBindly.clearErrors();
 
    if (!formData.firstName?.trim()) {
      DBindly.showError('First name is required', 'profile.firstName');
      return;
    }
 
    try {
      await updateProfile(formData);
      DBindly.setData({ profile: formData });
    } catch (error) {
      DBindly.showError('Failed to update profile');
    }
  }
});
 
// Monitor performance
function logMetrics(): void {
  const metrics: DBindlyMetrics = DBindly.getMetrics();
 
  console.table({
    'Total Requests': metrics.requestCount,
    'Cache Hits': metrics.cacheHits,
    'Cache Misses': metrics.cacheMisses,
    'Hit Rate': `${(metrics.cacheHitRate * 100).toFixed(1)}%`,
    'Avg Load Time': `${metrics.averageLoadTime.toFixed(0)}ms`,
    'Errors': metrics.errors
  });
}
 
// Initialize app
async function init(): Promise<void> {
  const user = await loadUserProfile('current');
 
  if (user) {
    console.log(`Welcome, ${user.profile.firstName}!`);
  }
 
  // Log metrics in development
  if (process.env.NODE_ENV === 'development') {
    setInterval(logMetrics, 30000);
  }
}
 
init();

Next Steps