Adding TypeScript to the Expo Project
Adding TypeScript to the Expo Project
~13 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026
From here on, we'll gradually migrate produktkatalog-app to TypeScript – EXACTLY as in "React for Professionals" chapters 40-43, the next four chapters cover EXCLUSIVELY the React Native INTEGRATION of TypeScript (typing props/hooks/navigation/API), not the TypeScript language itself – that's the subject of the separate, standalone TypeScript tutorial.
Expo already has TypeScript support built in
Unlike the plain Vite setup from "React for Professionals" chapter 40 (where tsconfig.json had to be created by hand), Expo AUTOMATICALLY recognizes TypeScript files as soon as they appear in the project – the only step needed is installing the right packages:
npx expo install typescript @types/reactLetting tsconfig.json get generated
Rename App.js to App.tsx (still WITHOUT adding types – a plain rename is enough as the first step) and restart the app (npx expo start). Expo automatically detects the missing tsconfig.json and creates a BASE version:
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true
}
}"extends": "expo/tsconfig.base" pulls in Expo's own base configuration, carefully tuned for React Native (JSX mode, module resolution, React Native-specific types) – considerably less manual configuration than the plain Vite setup. We add "strict": true ourselves – as explained in "React for Professionals" chapter 40, this is ALWAYS recommended for a new project.
Checking TypeScript errors: the standalone check
Unlike Vite (where noEmit: true was enough because esbuild ignores type checking), Metro (Expo's bundler) has NO built-in type checking – a TypeScript error does NOT automatically prevent the app from starting. Check manually with:
npx tsc --noEmitAchtung: This is an important difference from "React for Professionals": there, the Vite dev server often reported TypeScript errors directly in the browser overlay. With Expo, npx tsc --noEmit remains the most reliable way to check the ENTIRE project's type status – ideally as its own step in a CI pipeline (see chapter 16, testing) or as an editor integration (VS Code shows TypeScript errors live in the editor regardless, independent of the running bundler).
Migrating the first file: store/cartStore.js
Analogous to "React for Professionals" chapter 40, we start with a file WITHOUT JSX – store/cartStore.js becomes cartStore.ts:
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import AsyncStorage from '@react-native-async-storage/async-storage';
export interface CartItem {
sku: string;
name: string;
price: number;
}
interface CartState {
cart: CartItem[];
addProduct: (product: CartItem) => void;
removeProduct: (sku: string) => void;
}
export const useCartStore = create<CartState>()(
persist(
(set, get) => ({
cart: [],
addProduct(product) {
set({ cart: [...get().cart, product] });
},
removeProduct(sku) {
set({ cart: get().cart.filter((item) => item.sku !== sku) });
},
}),
{
name: 'cart-storage',
storage: createJSONStorage(() => AsyncStorage),
}
)
);Achtung: Notice create<CartState>()(persist(...)) – the EXTRA, empty parentheses () right after <CartState> are NOT a typo. Zustand's TypeScript API uses this "curried" call pattern (two consecutive function calls instead of one) so TypeScript can correctly infer the generic type through the persist middleware – without the second parentheses, TypeScript wouldn't reliably recognize the state type. This quirk didn't come up in "React for Professionals" chapter 42, since persist wasn't used there.
The pattern is otherwise identical to "React for Professionals" chapters 40/42: export interface CartItem as a reusable type we'll import in several files over the coming chapters.
Tipp: Migration order recommended as in "React for Professionals": small, isolated files (stores, utility functions) first, complex screens with many dependencies (like ProductListScreen) last – the rest of the app stays runnable throughout the gradual migration, .js and .tsx files coexist without issue.