# TypeScript `any` Type Policy

**Last Updated:** 2025-11-03  
**Status:** Active  
**Compliance:** ~92% (126/134 uses are acceptable)

---

## Overview

This document defines when `any` types are acceptable versus unacceptable in ThrillWiki. The goal is to maintain **type safety where it matters most** (user-facing components, API boundaries) while allowing pragmatic `any` usage for truly dynamic or generic scenarios.

---

## ✅ **ACCEPTABLE USES**

### 1. **Generic Utility Functions**
When creating truly generic utilities that work with any type:

```typescript
// ✅ GOOD - Generic tracking function
export async function invokeWithTracking<T = any>(
  functionName: string,
  payload: Record<string, any>
): Promise<InvokeResult<T>> {
  // Generic response handling
}
```

**Why acceptable:** The function genuinely works with any response type, and callers can provide specific types when needed.

### 2. **JSON Database Values**
For arbitrary JSON stored in database columns:

```typescript
// ✅ GOOD - Database versioning with arbitrary JSON
interface EntityVersion {
  old_value: any; // Could be any JSON structure
  new_value: any; // Could be any JSON structure
  changed_fields: string[];
}
```

**Why acceptable:** Database JSON columns can store any valid JSON. Using `unknown` would require type guards everywhere without adding safety.

### 3. **Temporary Composite Data**
For data that's validated by schemas before actual use:

```typescript
// ✅ GOOD - Temporary form data validated by Zod
interface ParkFormData {
  _tempNewPark?: any; // Validated by parkSchema before submission
  images: {
    uploaded: Array<{
      file?: File;
      url: string;
    }>;
  };
}
```

**Why acceptable:** The `any` is temporary and the data is validated by Zod schemas before being used in business logic.

### 4. **Format Utility Functions**
For functions that format various primitive types:

```typescript
// ✅ GOOD - Formats any primitive value for display
export function formatValue(value: any): string {
  if (value === null || value === undefined) return 'N/A';
  if (typeof value === 'boolean') return value ? 'Yes' : 'No';
  if (typeof value === 'number') return value.toLocaleString();
  if (value instanceof Date) return format(value, 'PPP');
  return String(value);
}
```

**Why acceptable:** The function truly handles any primitive type and returns a string. Type narrowing is handled internally.

### 5. **Error Objects in Catch Blocks**
We use `unknown` instead of `any`, then narrow:

```typescript
// ✅ GOOD - Error handling with unknown
try {
  await riskyOperation();
} catch (error: unknown) {
  const errorMessage = error instanceof Error ? error.message : String(error);
  edgeLogger.error('Operation failed', { error: errorMessage });
}
```

**Why acceptable:** Catching `unknown` and narrowing to specific types is the TypeScript best practice.

### 6. **Dynamic Form Data**
For forms with dynamic fields validated by Zod:

```typescript
// ✅ GOOD - Dynamic form data with Zod validation
const formSchema = z.object({
  name: z.string(),
  specs: z.record(z.any()), // Dynamic key-value pairs
});
```

**Why acceptable:** The `any` is constrained by Zod validation, and the fields are truly dynamic.

### 7. **Third-Party Library Types**
When libraries don't export proper types:

```typescript
// ✅ GOOD - Missing types from external library
import { SomeLibraryComponent } from 'poorly-typed-lib';

interface Props {
  config: any; // Library doesn't export ConfigType
}
```

**Why acceptable:** We can't control external library types. Document this with a comment.

### 8. **JSON to Form Data Conversions**
For complex transformations between incompatible type systems:

```typescript
// ✅ GOOD - Documented conversion between type systems
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const formData = jsonToFormData(submission.item_data as any);
// Note: Converting between JSON and form data requires type flexibility
```

**Why acceptable:** These conversions bridge incompatible type systems. Must be documented and marked with eslint-disable comment.

---

## ❌ **UNACCEPTABLE USES**

### 1. **Component Props**
Never use `any` for React component props:

```typescript
// ❌ BAD - Loses all type safety
interface RideHighlightsProps {
  ride: any;
}

// ✅ GOOD - Explicit interface
interface RideWithStats {
  id: string;
  name: string;
  max_speed_kmh?: number;
  max_height_meters?: number;
}

interface RideHighlightsProps {
  ride: RideWithStats;
}
```

**Why unacceptable:** Component props should be explicit to catch errors at compile time and provide autocomplete.

### 2. **State Variables**
Never use `any` for state hooks:

```typescript
// ❌ BAD
const [data, setData] = useState<any>(null);

// ✅ GOOD
interface FormData {
  name: string;
  description: string;
}
const [data, setData] = useState<FormData | null>(null);
```

**Why unacceptable:** State is the source of truth for your component. Type it properly.

### 3. **API Response Types**
Always define interfaces for API responses:

```typescript
// ❌ BAD
const fetchPark = async (id: string): Promise<any> => {
  const response = await supabase.from('parks').select('*').eq('id', id);
  return response.data;
};

// ✅ GOOD
interface Park {
  id: string;
  name: string;
  slug: string;
  location?: string;
}

const fetchPark = async (id: string): Promise<Park | null> => {
  const { data } = await supabase.from('parks').select('*').eq('id', id).single();
  return data;
};
```

**Why unacceptable:** API boundaries are where errors happen. Type them explicitly.

### 4. **Event Handlers**
Never use `any` for event handler parameters:

```typescript
// ❌ BAD
const handleClick = (event: any) => {
  event.preventDefault();
};

// ✅ GOOD
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
  event.preventDefault();
};
```

**Why unacceptable:** Event types provide safety and autocomplete for event properties.

### 5. **Function Parameters**
Avoid `any` in function signatures unless truly generic:

```typescript
// ❌ BAD
function processData(data: any) {
  return data.items.map((item: any) => item.name);
}

// ✅ GOOD
interface DataWithItems {
  items: Array<{ name: string }>;
}
function processData(data: DataWithItems) {
  return data.items.map(item => item.name);
}
```

**Why unacceptable:** Parameters define your function's contract. Type them explicitly.

---

## 📋 **Current Status**

### Acceptable `any` Uses (126 instances):
- Generic utility functions: `edgeFunctionTracking.ts` (12)
- JSON database values: `item_edit_history`, versioning tables (24)
- Temporary composite data: Form schemas with Zod validation (18)
- Format utility functions: `formatValue()`, display helpers (15)
- Error objects: All use `unknown` then narrow ✅
- Dynamic form data: Zod-validated records (32)
- Third-party library types: Uppy, MDXEditor (8)
- JSON to form conversions: Documented with comments (17)

### Fixed Violations (8 instances):
✅ Component props: `RideHighlights.tsx`, `TimelineEventEditorDialog.tsx`  
✅ Event handlers: `AdvancedRideFilters.tsx`, `AutocompleteSearch.tsx`  
✅ State variables: `EditHistoryAccordion.tsx`, `ReportsQueue.tsx`  
✅ Function parameters: `ValidationSummary.tsx`

---

## 🔍 **Review Process**

When adding new `any` types:

1. **Ask:** Can I define a specific interface instead?
2. **Ask:** Is this truly dynamic data (JSON, generic utility)?
3. **Ask:** Is this validated by a schema (Zod, runtime check)?
4. **If yes to 2 or 3:** Use `any` with a comment explaining why
5. **If no:** Define a specific type/interface

When reviewing code with `any`:

1. Check if it's in the "acceptable" list above
2. If not, request a specific type definition
3. If acceptable, ensure it has a comment explaining why

---

## 📚 **Related Documentation**

- [Type Safety Implementation Status](./TYPE_SAFETY_IMPLEMENTATION_STATUS.md)
- [Project Compliance Status](./PROJECT_COMPLIANCE_STATUS.md)
- [ESLint Configuration](../eslint.config.js)
- [TypeScript Configuration](../tsconfig.json)

---

## 🎯 **Success Metrics**

- **Current:** ~92% acceptable uses (126/134)
- **Goal:** Maintain >90% acceptable uses
- **Target:** All user-facing components have explicit types ✅
- **Enforcement:** ESLint warns on `@typescript-eslint/no-explicit-any`
