◉RORK LABJP
●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SDK 58 — Beta since 09/15 with no stable date yet; running projects in Expo Go has required login since 09/03●WIDGET — A write-up traces an iOS widget that renders black only in production builds●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SDK 58 — Beta since 09/15 with no stable date yet; running projects in Expo Go has required login since 09/03●WIDGET — A write-up traces an iOS widget that renders black only in production builds●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…
Articles/Dev Tools
⬡ Dev Tools/2026-04-28Intermediate

Five Fix Patterns for TypeScript Errors That Refuse to Leave Your Rork-Generated Code

When Rork-generated React Native code is buried in red squiggles and the AI keeps re-introducing the same TypeScript errors, these five patterns and their concrete fixes are the first places to look.

Rork585TypeScript8React Native239Type ErrorsDebugging11AI Code

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 error

Tell 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/lodash

If 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 mismatch

Centralise 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[] guaranteed

If 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.

Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

⬡ Dev Tools2026-09-04
EAS secret visibility does not keep a value out of your app — deciding prefix and visibility separately
The EXPO_PUBLIC_ prefix decides what ships inside your app; EAS visibility decides who can read it. Why stacking them blanks a value on OTA updates, and how to check your build.
⬡ Dev Tools2026-08-22
Every bulk replace exited zero. The damage was in the lines I did not delete
Run a bulk replace over generated code and the breakage lands on the neighbouring lines, not the matched ones. Here is what broke in a live project, and a dependency-free guard that checks the invariants a replace must preserve.
⬡ Dev Tools2026-08-14
Find the native edits expo prebuild will erase before you upgrade to SDK 57
Expo SDK 57 makes expo prebuild clear and regenerate ios and android by default. Here is how to audit your hand edits first, move them into config plugins, and why 57.0.9 matters for Reanimated apps.