When building apps with AI agents in Cursor or Claude Code, LLMs often default to loose TypeScript types (any) and silent exception swallowing inside try-catch blocks.
By default, an LLM prioritizes code that executes without breaking the immediate compiler scope, even if it creates severe production runtime bugs down the road.
The Problem: Loose Types & Silent Failure
// AI Default Output: Loose types and swallowed errors
const fetchUserProfile = async (userId: any) => {
try {
const res = await fetch("/api/users/" + userId);
const data = await res.json();
return data;
} catch (err) {
console.log("Error fetching profile", err);
return null; // Silent failure swallowed!
}
};
The Solution: Explicit Prompt Directives
To force Cursor or Claude Code to output production-grade error boundaries and strict TypeScript interfaces, add these explicit directives to your system prompt:
Refactored Production Pattern
import { z } from 'zod';
const UserProfileSchema = z.object({
id: z.string(),
email: z.string().email(),
subscriptionStatus: z.enum(['active', 'canceled', 'trialing']),
});
export type UserProfile = z.infer<typeof UserProfileSchema>;
export async function fetchUserProfile(userId: string): Promise<UserProfile> {
const res = await fetch("/api/users/" + userId);
if (!res.ok) {
throw new Error("Failed to fetch user profile: " + res.statusText);
}
const json = await res.json();
return UserProfileSchema.parse(json);
}
Audit Recommendation: Don't rely solely on manual code reviews. Run an automated static AST scan to verify missing error boundaries before deploying to production.