Optimizing GPT-4o System Prompts for Complex TypeScript Refactoring Tasks
.cursorrules file is where the real magic happens for TypeScript refactoring, but most people just dump a generic "You are an expert TS developer" prompt in there. For complex refactoring—like migrating a legacy Redux state tree to Zustand or decoupling tight dependencies in a large monorepo—GPT-4o tends to get "lazy" and leave // ... rest of the code comments, which is a nightmare when you're trying to apply changes across 20 files.The trick is to move away from descriptive prompts and toward structural constraints. Instead of telling it to "be precise," tell it exactly how to handle the AST (Abstract Syntax Tree) logic.
I've found that forcing the AI to follow a "Plan-Verify-Execute" loop within the system prompt drastically reduces hallucinations in type definitions. Here is the specific prompt block I've integrated into my project rules to stop the "lazy coding" habit:
# Refactoring Protocol
1. Analyze the existing type hierarchy and identify all downstream dependencies of the target symbol.
2. Before writing code, output a <refactor_plan> block listing every file that requires a change.
3. When modifying TS files, you MUST output the full function or class body. NEVER use comments like "// existing logic here" to omit code.
4. If a change introduces a potential type mismatch, you must explicitly propose a Type Guard or a Zod schema to handle the runtime validation.Beyond the prompt, the way you feed context to the model is where most devs fail. If you're refactoring a complex interface, don't just @ the file. Use the terminal to grep the usages first and paste the relevant snippets into the chat. GPT-4o is much better at refactoring when it sees the call site and the definition side-by-side in the same window, rather than relying on its own indexing which can sometimes miss edge cases in deeply nested generics.
One major "gotcha" I hit: GPT-4o loves to suggest any or unknown when it gets confused by complex mapped types. To kill this, I added a strict "No-Any" constraint to my system prompt:
// System Constraint:
Strictly forbid the use of 'any'. If a type is truly dynamic, use a generic <T> or a union of known types.
If the type is unknown, use 'unknown' and provide a type-narrowing function.In terms of productivity gains, I've shifted my workflow to "Iterative Narrowing." Instead of asking for a massive refactor in one go, I use a prompt that forces the AI to handle one layer of the abstraction at a time:
Step 1: Type Definition Sync (Update interfaces/types first).
Step 2: Logic Migration (Move the implementation).
Step 3: Cleanup (Remove dead code and fix imports).
This prevents the model from losing the thread of the logic halfway through a 200-line file. When combined with Cursor's "Composer" mode (Cmd+I), this structured approach allows me to refactor entire modules in about 15 minutes—a task that used to take me an entire afternoon of manual searching and replacing.
All Replies (0)
No replies yet — be the first!
