TypeScript in React Native
TypeScript is the default for React Native jobs — add type labels to props, state and navigation so the editor catches mistakes before the app even runs.
What you will learn
- Type a component’s props with an interface
- Type useState so the value stays the right shape
- Type navigation route params so screens cannot get bad data
Why every job asks for TypeScript
TypeScript is JavaScript with types — tiny labels that say what kind of value something is (a number, a string, a list of users). The editor reads those labels and warns you the moment you use a value the wrong way, before the app runs on a phone. In 2026, almost every React Native job description lists TypeScript, and most paid courses teach it from the first lesson. A JavaScript-only app is below the current hiring bar.
The good news: you already know React Native. TypeScript does not change how components, props or useState work — it just lets you describe what each one holds so mistakes get caught early. Think of it as a spell-checker for your data.
Expo projects come TypeScript-ready: name your files .tsx (a .tsx file is a TypeScript file that also contains JSX, the <View>-style markup) instead of .js, and you are typing. New Expo apps already include the small tsconfig.json settings file for you.
Typing props with an interface
A component’s props are the values passed into it, like <Greeting name="Aanya" />. In TypeScript you first describe the shape of those props with an interface (a named list of fields and their types), then tell the component to use it. Here is a small greeting card that expects a name string and an age number:
import { View, Text } from 'react-native';
interface GreetingProps {
name: string;
age: number;
}
function Greeting({ name, age }: GreetingProps) {
return (
<View>
<Text>Hi {name}, you are {age} years old.</Text>
</View>
);
}
// Using it:
<Greeting name="Aanya" age={20} />Note: Output:
On the phone: Hi Aanya, you are 20 years old.
If you wrote <Greeting name="Aanya" /> (forgetting age), or age="20" (a string instead of a number), the editor underlines it in red before you ever run the app and tells you exactly what is wrong.
Line by line, what the interface bought us:
interface GreetingProps { ... }names a shape: anamethat must be astringand anagethat must be anumber.{ name, age }: GreetingPropstells the component "the props you receive match this shape" — so inside,nameis known to be a string andagea number.- Anyone using
<Greeting />now gets autocomplete for the props and a clear error if they pass the wrong thing or forget one.
Typing useState
Most of the time TypeScript figures out the type of a useState on its own from the starting value. useState(0) is clearly a number; useState('') is clearly a string. You only add a type yourself when the starting value does not tell the whole story — for example an array that starts empty, or a value that might be missing.
Here we keep a list of users. It starts as an empty array [], which on its own does not say what the array holds, so we spell it out with useState<User[]> (read: "state holding an array of User"):
import { useState } from 'react';
interface User {
id: number;
name: string;
}
const [users, setUsers] = useState<User[]>([]);
// Later — this is allowed:
setUsers([{ id: 1, name: 'Aanya' }]);
// This is caught as an error:
setUsers([{ id: 1, title: 'Aanya' }]);Note: Output:
No screen output — this is about safety. The second setUsers is underlined in red: a User needs a name, not a title. TypeScript stops you from saving the wrong shape, which would otherwise crash later when you read user.name.
Typing navigation route params
Remember passing data between screens with route.params? Without types, nothing stops you from reading a param that was never sent, which crashes at runtime. TypeScript lets you list every screen and the params it expects in one place, so the editor checks both the navigate call and the route.params read.
You create a type that maps each screen name to its params. undefined means "this screen takes no params":
import { NativeStackScreenProps } from '@react-navigation/native-stack';
// 1) List every screen and the params it expects
type RootStackParamList = {
Home: undefined;
Details: { itemId: number; itemName: string };
};
// 2) Type one screen's props from that list
type DetailsProps = NativeStackScreenProps<RootStackParamList, 'Details'>;
function DetailsScreen({ route }: DetailsProps) {
// route.params is now known to have itemId and itemName
return <Text>You picked {route.params.itemName}</Text>;
}Note: Output:
On the phone (when navigated to with an item): You picked Milk
Now navigation.navigate('Details', { itemId: 1, itemName: 'Milk' }) is checked too — forget itemId or send a string where a number belongs and the editor flags it. And route.params.itemNme (a typo) is caught instead of silently being undefined.
How the pieces fit together, step by step
Here is the whole "add TypeScript" journey for a screen, in order, so you can repeat it on any component:
- Name your file
.tsxso it accepts both types and JSX. - Write an
interface(ortype) describing the props the component receives — each field and its type. - Annotate the component’s parameter with that interface:
function Screen({ ... }: ScreenProps). - For state, let TypeScript infer simple values; add
useState<Shape>(...)only when the starting value is empty or could be missing. - For navigation, write one
ParamListtype mapping screen names to their params, and type each screen withNativeStackScreenProps. - Watch the editor: red underlines now appear the instant a value has the wrong shape — fix them before you ever open the app on a phone.
Tip: Prefer typing data shapes (props, API responses, route params) first — that is where wrong-shape bugs hurt most. You rarely need to type every local variable; TypeScript infers most of them for you.
Watch out: A red underline is a compile-time warning, not a crash. The app can still run with type errors during development, but you should fix them — an ignored type error is a real bug waiting to bite a user later.
Q. Why do you sometimes write useState<User[]>([]) instead of just useState([])?
[] gives TypeScript no clue about the item shape, so you spell it out with useState<User[]> to keep the array type-safe.✍️ Practice
- Add a
ProfilePropsinterface (name: string, isOnline: boolean) and type aProfilecomponent with it. - Type a
useStatethat starts asnulland later holds aUserobject (hint:useState<User | null>(null)).
🏠 Homework
- Convert one screen from an earlier lesson to
.tsx: add a props interface, type itsuseState, and fix every red underline the editor shows.