You open a Rork project in VS Code, and the editor lights up with red squiggles everywhere. You ask the AI to "fix the type errors," but instead of fixing them it shuffles the same errors around the file. The compile loop never stabilises, and you slowly start losing trust in the codebase.
After shipping a few apps with Rork, I noticed that the type errors that refuse to die almost always fall into a small handful of patterns. The trick is to fix the type yourself, in one place, just once — and then let the AI continue from a clean foundation. Once the model has a working example to imitate, the next ten generations come out far closer to correct. The following five patterns cover the cases I run into most often, in the order I usually tackle them.
Pattern 1: Implicit any from Untyped Props
The most common one is forgotten prop types. The AI is in a hurry to ship a feature and leaves the prop annotations for later, which means later usually never arrives.
// ❌ Typical Rork output — implicit any everywhere
export function ProductCard({ name, price, onPress }) {
return (
<Pressable onPress={onPress}>
<Text>{name}</Text>
<Text>${price.toFixed(2)}</Text>
</Pressable>
);
}
// strict mode floods with "Parameter 'name' implicitly has an 'any' type"Pull the prop shape into a named alias and annotate the destructured argument. A named type takes about ten seconds to write and pays for itself the first time you reuse the component.
// ✅ Make the contract explicit
type ProductCardProps = {
name: string;
price: number;
onPress: () => void;
};
export function ProductCard({ name, price, onPress }: ProductCardProps) {
return (
<Pressable onPress={onPress}>
<Text>{name}</Text>
<Text>${price.toFixed(2)}</Text>
</Pressable>
);
}When you ask the AI to extend this component next, it tends to keep the type intact. The annotation works as documentation that the model can read, which means each subsequent edit drifts less from your intent.
Pattern 2: useState That Inferred the Wrong Type
useState(null) and useState([]) look harmless but they get inferred as null or never[], and any later assignment will fail.
// ❌ inferred as never[] — push fails
const [items, setItems] = useState([]);
setItems([...items, { id: 1, name: "Coffee" }]); // type error
// ❌ inferred as null only
const [user, setUser] = useState(null);
setUser({ name: "Masaki" }); // type errorTell TypeScript what you intend to store via a generic. The generic is also a hint to the AI about what data flows through this state.
// ✅ Declare the future shape up front
type Item = { id: number; name: string };
const [items, setItems] = useState<Item[]>([]);
type User = { name: string } | null;
const [user, setUser] = useState<User>(null);Fixing this often resolves a chain of errors all the way down through your API handlers, because the inferred type was leaking into the response handler too. If you also have re-render glitches in the same component, the Rork state-not-updating troubleshooting guide is worth a quick read while you are in the area.
Pattern 3: Missing Type Definitions for Third-Party Libraries
When Rork pulls in a large dependency like Expo or React Navigation, the matching type package occasionally goes missing. If VS Code says "Could not find a declaration file for module '...'", this is your culprit. The runtime might still work, but the type checker will block your CI build.
# ❌ Common error
# Could not find a declaration file for module 'react-native-svg'
# ✅ Install via Expo when the SDK manages the version
npx expo install react-native-svg
# For older libraries that ship without bundled types
npm install --save-dev @types/lodashIf you stay inside Rork, prompt the AI explicitly with "regenerate so that react-native-svg and its type definitions are installed." It will produce the correct expo install flow more reliably than a vague "fix the imports." The reason is that expo install resolves the SDK-compatible version, while npm install may pull in a version that breaks at runtime even though the types compile cleanly.
A subtle pitfall: some libraries publish their own types under the same package name (no @types/... package needed). If you blindly add @types/somepackage and the package already ships types, you can introduce duplicate declarations. Check the library's README before installing the @types/ companion.
Pattern 4: Inconsistent React Navigation Param Types
Navigation param types are the single biggest source of friction in AI-generated React Native code. The model tends to redefine the param shape on every screen, and the type system catches the divergence at every navigate call.
// ❌ Each screen defines its own version of the same params
type DetailParams = { productId: number };
navigation.navigate("Detail", { productId: "abc" }); // string vs number mismatchCentralise the param map in a single file. This is the same trick as Pattern 1 — once the canonical type exists, the AI is more likely to imitate it.
// ✅ One source of truth for the entire stack
export type RootStackParamList = {
Home: undefined;
Detail: { productId: number };
Profile: { userId: string };
};
type DetailScreenProps = NativeStackScreenProps<RootStackParamList, "Detail">;
export function DetailScreen({ route, navigation }: DetailScreenProps) {
const { productId } = route.params; // strongly typed
return <Text>{productId}</Text>;
}Place RootStackParamList near App.tsx and Rork will keep referencing it in subsequent generations. If you have a tab navigator nested inside a stack navigator, define a separate MainTabParamList and compose the two — do not collapse them into a single type, or the inner screens will lose their typing on navigation.getParent().
Pattern 5: When the AI Hides Errors Behind as any
The dangerous one. After enough failed attempts, the AI sometimes silences the compiler with as any. The build passes, but the bug ships to production and surfaces as a TypeError: Cannot read property 'X' of undefined once a real user touches the screen.
// ❌ The lazy escape
const data = (response as any).items.map((x: any) => x.name);Make the problem visible first. A short grep is enough to see how big the problem is.
# Count every "as any" in the codebase
grep -rn "as any" src/ | wc -l
# Inspect them
grep -rn "as any" src/Then replace them with real validation. Zod is a particularly good fit because it gives you both runtime safety and type inference in one declaration.
// ✅ Validate the shape at the boundary
import { z } from "zod";
const ItemSchema = z.object({ id: z.number(), name: z.string() });
const ResponseSchema = z.object({ items: z.array(ItemSchema) });
const parsed = ResponseSchema.parse(response);
const names = parsed.items.map((x) => x.name); // string[] guaranteedIf the AI keeps re-introducing as any, prompt it with "rewrite without as any, using a type guard or schema validator." Adding a one-line note to your project's main README.md such as "Never use as any. Use Zod or a type guard." also nudges future generations, because Rork picks up that file as project context. If you also notice the AI overwriting parts of your code on every edit, the guide on stopping Rork's AI from breaking existing code explains how to scope its changes.
A Quick Note on tsconfig.json
Before you go pattern-hunting, double-check that strict: true is set in your tsconfig.json. Rork sometimes generates a project with strict: false to silence early errors, which means many of the patterns above will not even surface as warnings. Turning strict mode on once and dealing with the resulting wave of errors is painful for an hour but keeps the codebase honest after that.
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true
}
}noUncheckedIndexedAccess in particular catches a class of bugs the AI loves to create — assuming array[0] is always defined when it might be undefined.
Closing — Type One File Properly Before You Type the Rest
Five patterns sound like a lot, but you do not have to fix them all at once. The approach that has worked best for me is to pick the single file you touch most often and type it perfectly. That file becomes a template the AI imitates on its next run, and the surrounding files start improving on their own.
When TypeScript errors are tangled up with useEffect warnings, isolate the effect issue first using the Rork useEffect infinite-loop debugging guide, then come back to the type fixes. Treating the two problem classes separately is almost always faster than trying to solve them together.