Skip to content

STRICT TYPING GUIDE - NO 'any' OR 'unknown' ALLOWED

Note: This is a legacy document. For current requirements, see Zero Tolerance Rules.

Table of Contents


Core Principles

❌ NEVER USE

typescript
any
unknown
Record<string, any>
Record<string, unknown>

✅ ALWAYS USE

typescript
// Define explicit interfaces
interface MyData {
  field1: string;
  field2: number;
}

// Or use Zod schemas with inferred types
const MyDataSchema = z.object({
  field1: z.string(),
  field2: z.number(),
});
type MyData = z.infer<typeof MyDataSchema>;

Error Handling

❌ WRONG - Using 'unknown'

typescript
try {
  await something();
} catch (error: unknown) {  // FORBIDDEN
  console.error(error);
}

✅ CORRECT - Explicit Error Types

typescript
import { isError, isErrorResponse, getErrorMessage, type ErrorResponse } from '@/types/index.js';

try {
  await something();
} catch (error: Error | ErrorResponse) {  // Explicit union type
  if (isError(error)) {
    console.error(error.message);
    logger.error(error.stack);
  } else if (isErrorResponse(error)) {
    console.error(error.message);
  } else {
    // TypeScript will catch this - error must be Error | ErrorResponse
  }
}

Define ErrorResponse Interface

typescript
// In src/types/errors.ts
export interface ErrorResponse {
  message: string;
  code?: string;
  status?: number;
  details?: ErrorDetails;
  stack?: string;
}

export interface ErrorDetails {
  field?: string;
  constraint?: string;
  value?: string | number;
}

// Type guards
export function isError(value: Error | ErrorResponse | object | string): value is Error {
  return value instanceof Error;
}

export function isErrorResponse(value: Error | ErrorResponse | object | string): value is ErrorResponse {
  return (
    typeof value === 'object' &&
    value !== null &&
    'message' in value &&
    typeof (value as ErrorResponse).message === 'string'
  );
}

export function getErrorMessage(error: Error | ErrorResponse): string {
  if (isError(error)) return error.message;
  if (isErrorResponse(error)) return error.message;
  return 'Unknown error';
}

JSON Parsing

❌ WRONG - Using 'any' or 'unknown'

typescript
const data = JSON.parse(jsonString);  // Returns 'any' - FORBIDDEN
const data: unknown = JSON.parse(jsonString);  // FORBIDDEN

✅ CORRECT - Parse with Explicit Type

typescript
import { parseJSON } from '@/types/index.js';
import { UserDataSchema, type UserData } from '@/types/user.js';

// Method 1: Using helper function with Zod
const data: UserData = parseJSON(jsonString, UserDataSchema);

// Method 2: Direct Zod parse
const rawData: string | number | boolean | object | null = JSON.parse(jsonString);
const data: UserData = UserDataSchema.parse(rawData);

Define parseJSON Helper

typescript
// In src/utils/json.ts
import type { z } from 'zod';

export function parseJSON<T extends z.ZodType>(
  json: string,
  schema: T
): z.infer<T> {
  try {
    const parsed: string | number | boolean | object | null = JSON.parse(json);
    return schema.parse(parsed);
  } catch (error: Error | ErrorResponse) {
    throw new Error(`JSON parsing failed: ${getErrorMessage(error)}`);
  }
}

Database JSON Fields

❌ WRONG - Casting to 'any'

typescript
const metrics = post.performanceMetrics as any;  // FORBIDDEN
const context = post.generationContext as unknown;  // FORBIDDEN

✅ CORRECT - Explicit Type with Validation

typescript
import { validateJsonField } from '@/types/index.js';
import { 
  PerformanceMetricsSchema, 
  type PerformanceMetrics 
} from '@/types/metrics.js';

// Method 1: Validate and get typed data
const metrics: PerformanceMetrics | null = validateJsonField(
  post.performanceMetrics,
  PerformanceMetricsSchema
);

if (metrics !== null) {
  console.log(metrics.impressions); // Type-safe!
}

// Method 2: Direct Zod validation
const metrics: PerformanceMetrics = PerformanceMetricsSchema.parse(
  post.performanceMetrics
);

Define validateJsonField Helper

typescript
// In src/utils/validation.ts
import type { z } from 'zod';

export function validateJsonField<T extends z.ZodType>(
  value: string | number | boolean | object | null | undefined,
  schema: T
): z.infer<T> | null {
  if (value === null || value === undefined) {
    return null;
  }
  
  const result = schema.safeParse(value);
  return result.success ? result.data : null;
}

External API Responses

❌ WRONG - Using 'any' for API response

typescript
const response = await fetch(url);
const data: any = await response.json();  // FORBIDDEN

✅ CORRECT - Define Platform Response Types

typescript
import { LinkedInAnalyticsSchema, type LinkedInAnalyticsResponse } from '@/types/platforms.js';

async function fetchLinkedInAnalytics(postId: string): Promise<LinkedInAnalyticsResponse> {
  const response = await fetch(`https://api.linkedin.com/posts/${postId}/analytics`);
  const rawData: string | number | boolean | object | null = await response.json();
  
  // Validate the response
  const data: LinkedInAnalyticsResponse = LinkedInAnalyticsSchema.parse(rawData);
  return data;
}

Define Platform Response Types

typescript
// In src/types/platforms.ts
import { z } from 'zod';

export const LinkedInAnalyticsSchema = z.object({
  impressions: z.number().int().nonnegative(),
  likes: z.number().int().nonnegative(),
  comments: z.number().int().nonnegative(),
  shares: z.number().int().nonnegative(),
  engagement_rate: z.number().nonnegative(),
  clicks: z.number().int().nonnegative().optional(),
});

export type LinkedInAnalyticsResponse = z.infer<typeof LinkedInAnalyticsSchema>;

// Do this for EVERY platform
export const TwitterAnalyticsSchema = z.object({
  impressions: z.number().int().nonnegative(),
  retweets: z.number().int().nonnegative(),
  replies: z.number().int().nonnegative(),
  likes: z.number().int().nonnegative(),
  url_clicks: z.number().int().nonnegative().optional(),
});

export type TwitterAnalyticsResponse = z.infer<typeof TwitterAnalyticsSchema>;

Function Parameters

❌ WRONG - Using 'any' for parameters

typescript
function processData(data: any): void {  // FORBIDDEN
  console.log(data.field);
}

✅ CORRECT - Explicit Parameter Types

typescript
interface ProcessDataInput {
  field: string;
  value: number;
  optional?: boolean;
}

function processData(data: ProcessDataInput): void {
  console.log(data.field); // Type-safe!
}

// Or with Zod schema
const ProcessDataInputSchema = z.object({
  field: z.string(),
  value: z.number(),
  optional: z.boolean().optional(),
});

type ProcessDataInput = z.infer<typeof ProcessDataInputSchema>;

Catch Blocks

❌ WRONG - Using 'unknown' in catch

typescript
try {
  await riskyOperation();
} catch (error: unknown) {  // FORBIDDEN
  // Can't do anything safely with unknown
}

✅ CORRECT - Explicit Error Union Types

typescript
import { type ErrorResponse, isError, isErrorResponse, getErrorMessage } from '@/types/index.js';

try {
  await riskyOperation();
} catch (error: Error | ErrorResponse) {
  // Type guard to narrow the type
  if (isError(error)) {
    console.error(`Error: ${error.message}`);
    logger.error(error.stack);
  } else if (isErrorResponse(error)) {
    console.error(`API Error ${error.code}: ${error.message}`);
    logger.error(error.details);
  }
  
  // Or use helper
  const message: string = getErrorMessage(error);
  console.error(message);
}

Type Guards

❌ WRONG - Checking 'unknown'

typescript
function isValidData(data: unknown): data is MyType {  // FORBIDDEN
  return typeof data === 'object';
}

✅ CORRECT - Explicit Type Guards

typescript
interface MyType {
  id: string;
  name: string;
  value: number;
}

function isMyType(
  data: object | string | number | null | undefined
): data is MyType {
  return (
    typeof data === 'object' &&
    data !== null &&
    'id' in data &&
    typeof (data as MyType).id === 'string' &&
    'name' in data &&
    typeof (data as MyType).name === 'string' &&
    'value' in data &&
    typeof (data as MyType).value === 'number'
  );
}

// Usage
const data: object | string | number | null = getSomeData();
if (isMyType(data)) {
  console.log(data.name); // Type-safe!
}

// Or better: use Zod
const result = MyTypeSchema.safeParse(data);
if (result.success) {
  console.log(result.data.name); // Type-safe!
}

Platform-Specific Types

Create Exhaustive Type Definitions

typescript
// src/types/platforms.ts

// LinkedIn
export interface LinkedInAnalyticsResponse {
  impressions: number;
  likes: number;
  comments: number;
  shares: number;
  engagement_rate: number;
  clicks?: number;
}

export const LinkedInAnalyticsSchema = z.object({
  impressions: z.number().int().nonnegative(),
  likes: z.number().int().nonnegative(),
  comments: z.number().int().nonnegative(),
  shares: z.number().int().nonnegative(),
  engagement_rate: z.number().nonnegative(),
  clicks: z.number().int().nonnegative().optional(),
});

// Twitter/X
export interface TwitterAnalyticsResponse {
  impressions: number;
  retweets: number;
  replies: number;
  likes: number;
  url_clicks?: number;
}

export const TwitterAnalyticsSchema = z.object({
  impressions: z.number().int().nonnegative(),
  retweets: z.number().int().nonnegative(),
  replies: z.number().int().nonnegative(),
  likes: z.number().int().nonnegative(),
  url_clicks: z.number().int().nonnegative().optional(),
});

// Facebook
export interface FacebookAnalyticsResponse {
  impressions: number;
  reactions: number;
  comments: number;
  shares: number;
  clicks?: number;
}

// Instagram
export interface InstagramAnalyticsResponse {
  impressions: number;
  likes: number;
  comments: number;
  saves: number;
  reach?: number;
}

// ... Define for ALL platforms

AI Code Generation Guidelines

When using AI to generate code, ALWAYS follow these patterns:

Pattern 1: External API Call

typescript
// TEMPLATE for AI code generation
async function fetch{Platform}{Resource}(
  accessToken: string,
  resourceId: string
): Promise<{Platform}{Resource}Response> {
  const response = await fetch(`https://api.{platform}.com/{endpoint}`);
  
  if (!response.ok) {
    throw new Error(`{Platform} API error: ${response.statusText}`);
  }
  
  const rawData: string | number | boolean | object | null = await response.json();
  const data: {Platform}{Resource}Response = {Platform}{Resource}Schema.parse(rawData);
  
  return data;
}

Pattern 2: Database JSON Field Access

typescript
// TEMPLATE for AI code generation
import { validateJsonField } from '@/utils/validation.js';
import { {Type}Schema, type {Type} } from '@/types/{category}.js';

const field: {Type} | null = validateJsonField(
  record.{fieldName},
  {Type}Schema
);

if (field !== null) {
  // Use field safely
  console.log(field.property);
}

Pattern 3: Error Handling

typescript
// TEMPLATE for AI code generation
import { type ErrorResponse, isError, getErrorMessage } from '@/types/index.js';

try {
  await operation();
} catch (error: Error | ErrorResponse) {
  if (isError(error)) {
    logger.error(`Operation failed: ${error.message}`, {
      stack: error.stack,
    });
  } else {
    logger.error(`Operation failed: ${getErrorMessage(error)}`);
  }
  throw error;
}

Pattern 4: API Route Handler

typescript
// TEMPLATE for AI code generation
import { z } from 'zod';

const RequestSchema = z.object({
  field1: z.string(),
  field2: z.number(),
});

type RequestType = z.infer<typeof RequestSchema>;

const ResponseSchema = z.object({
  result: z.string(),
  success: z.boolean(),
});

type ResponseType = z.infer<typeof ResponseSchema>;

export async function handler(
  req: Request,
  res: Response
): Promise<void> {
  try {
    // Validate input
    const input: RequestType = RequestSchema.parse(req.body);
    
    // Process
    const result: string = await processRequest(input);
    
    // Build response
    const response: ResponseType = {
      result,
      success: true,
    };
    
    // Validate output (catches bugs)
    const validatedResponse: ResponseType = ResponseSchema.parse(response);
    
    res.json(validatedResponse);
  } catch (error: Error | ErrorResponse | z.ZodError) {
    if (error instanceof z.ZodError) {
      res.status(400).json({
        error: 'Validation failed',
        details: error.format(),
      });
    } else {
      res.status(500).json({
        error: getErrorMessage(error),
      });
    }
  }
}

Checklist for ALL New Code

Before committing, verify:

  • [ ] No any types anywhere
  • [ ] No unknown types anywhere
  • [ ] All function parameters have explicit types
  • [ ] All function return types are explicit
  • [ ] All catch blocks use Error | ErrorResponse
  • [ ] All JSON.parse() calls use parseJSON() helper
  • [ ] All Prisma JSON fields use validateJsonField()
  • [ ] All external API responses have defined types
  • [ ] All types have Zod schemas for validation
  • [ ] Error handling uses type guards

Quick Reference

Instead of This ❌

typescript
any
unknown
Record<string, any>
as any
catch (error: unknown)
JSON.parse() // returns any

Use This ✅

typescript
interface MyType { ... }
type MyType = z.infer<typeof MyTypeSchema>
Record<string, string | number | boolean>
as MyType // with explicit type
catch (error: Error | ErrorResponse)
parseJSON(json, MyTypeSchema)

Getting Help

  1. Check this guide first
  2. Look at existing code patterns in src/types/
  3. All types should be in src/types/{category}/
  4. All schemas should be colocated with types
  5. Ask in #engineering before using workarounds

REMEMBER: There is NO valid use case for any or unknown in this codebase.If you think you need it, you need to define a proper type instead.


Last Updated: January 2026Zero Tolerance for 'any' and 'unknown'

TendSocial Documentation