Skip to main content

Working with TypeScript

1. Configuring TypeScript

Example: Initial Setup

npm install -g typescript
npm install --save-dev @types/react @types/react-native
Configuration File Location Description
tsconfig.json project-root/tsconfig.json Root configuration file for TypeScript compiler options

Example: Basic tsconfig.json

{
  "compilerOptions": {
    "target": "es2017",
    "lib": [
      "es6"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "react-native"
  },
  "include": [
    "src"
  ],
  "exclude": [
    "node_modules"
  ]
}
Note: Ensure jsx is set to react-native for React Native projects.

Example: Enabling Strict Mode

{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true
  }
}

2. Typing Component Props

Prop Type Description
children ReactNode Content passed as children to the component
className string HTML class attribute for styling
style ViewStyle | TextStyle | ImageStyle Inline styles for the component

Example: Typing a Component with Props

import React from 'react';
import { Text, View } from 'react-native';

interface GreetingProps {
  name: string;
  age?: number;
}

const Greeting: React.FC<GreetingProps> = ({ name, age }) => (
  <View>
    <Text>Hello, {name}!</Text>
    {age && <Text>Age: {age}</Text>}
  </View>
);

export default Greeting;

Example: Using Children with Type

import React from 'react';
import { View } from 'react-native';

interface ContainerProps {
  children: React.ReactNode;
}

const Container: React.FC<ContainerProps> = ({ children }) => (
  <View style={{ padding: 10 }}>
    {children}
  </View>
);

export default Container;

3. Typing Navigation Params

When using TypeScript with React Navigation, it's crucial to type your navigation parameters to ensure type safety and better developer experience.

Parameter Type Description
navigation NavigationProp<ParamListBase> The navigation object provided by React Navigation
route RouteProp<ParamListBase> The route object containing the current screen's parameters

Example: Typing Navigation Parameters

import { NavigationProp, RouteProp } from '@react-navigation/native';
import { StackNavigationProp } from '@react-navigation/stack';

type HomeScreenParams = {
  userId: string;
  userName: string;
};

type HomeScreenNavigationProp = StackNavigationProp<HomeStackParamList, 'Home'>;
type HomeScreenRouteProp = RouteProp<HomeStackParamList, 'Home'>;

const HomeScreen: React.FC<{
  navigation: HomeScreenNavigationProp;
  route: HomeScreenRouteProp;
}> = ({ navigation, route }) => {
  const { userId, userName } = route.params;
  
  return (
    <View>
      <Text>User ID: {userId}</Text>
      <Text>User Name: {userName}</Text>
    </View>
  );
};

Example: Defining Navigation Parameter Types

import { ParamListBase } from '@react-navigation/native';

export type HomeStackParamList = {
  Home: {
    userId: string;
    userName: string;
  };
  Profile: {
    userId: string;
  };
};

export type ProfileStackParamList = {
  Profile: {
    userId: string;
    userName: string;
  };
};

4. Typing Hooks

Hook Type Signature Description
useState useState<T>(initialValue: T | (() => T)): [T, Dispatch<SetStateAction<T>>] State hook with generic type T
useEffect useEffect(effect: () => void | (() => void), deps?: DependencyList): void Effect hook with optional dependencies
useContext useContext<T>(context: Context<T>): T Context hook with generic type T
useReducer useReducer<S, A>(reducer: Reducer<S, A>, initialState: S): [S, Dispatch<A>] Reducer hook with state and action types
useCallback useCallback<T>(callback: T, deps: DependencyList): T Callback hook with dependency array
useMemo useMemo<T>(factory: () => T, deps: DependencyList): T Memoized value with dependency array
useRef useRef<T>(initialValue: T): MutableRefObject<T> Ref hook with generic type T

Example: Typing useState

interface User {
  id: number;
  name: string;
}

const [user, setUser] = useState<User | null>(null);

Example: Typing useEffect

const [count, setCount] = useState(0);

useEffect(() => {
  document.title = `Count: ${count}`;
}, [count]);

Example: Typing useContext

interface ThemeContextType {
  theme: 'light' | 'dark';
  toggleTheme: () => void;
}

const ThemeContext = createContext<ThemeContextType>({
  theme: 'light',
  toggleTheme: () => {},
});

const { theme } = useContext(ThemeContext);

Example: Typing useReducer

type Action = { type: 'INCREMENT' } | { type: 'DECREMENT' };

const reducer = (state: number, action: Action): number => {
  switch (action.type) {
    case 'INCREMENT': return state + 1;
    case 'DECREMENT': return state - 1;
    default: return state;
  }
};

const [count, dispatch] = useReducer(reducer, 0);

5. Using Generic Components

Generic components in React Native with TypeScript allow you to create reusable components that work with multiple types, enhancing type safety and code reusability.

Property Type Description
children React.ReactNode Render content inside the component
style StyleProp<ViewStyle> Styling for the component

Example: Generic Component with Props

import React from 'react';
import { View, Text, StyleSheet } from 'react-native';

interface GenericComponentProps<T> {
  data: T;
  renderItem: (item: T) => React.ReactNode;
}

const GenericComponent = <T>(props: GenericComponentProps<T>) => {
  return (
    <View style={styles.container}>
      {props.data.map((item, index) => (
        <View key={index}>
          {props.renderItem(item)}
        </View>
      ))}
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    padding: 10,
  },
});

Example: Using Generic Component with Specific Type

interface User {
  id: number;
  name: string;
}

const users: User[] = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
];

const UserList = () => {
  return (
    <GenericComponent<User>
      data={users}
      renderItem={(user) => (
        <Text style={styles.text}>{user.name}</Text>
      )}
    />
  );
};

const styles = StyleSheet.create({
  text: {
    fontSize: 16,
  },
});

6. Typing Context Values

Property Type Description
ContextType React.ContextType<T> Type of the context value provided by React.createContext
useContext (context: React.Context<T>) => T Hook to consume a context value

Example: Typing Context Values

import React, { createContext, useContext } from 'react';

interface ThemeContextType {
  theme: 'light' | 'dark';
  toggleTheme: () => void;
}

const ThemeContext = createContext<ThemeContextType>({
  theme: 'light',
  toggleTheme: () => {},
});

const useTheme = () => useContext(ThemeContext);

export default useTheme;
Note: When typing context values, ensure that the default value matches the expected type to avoid runtime errors.

7. Typing Native Modules

When working with native modules in React Native, TypeScript can help ensure type safety and better developer experience. Native modules are typically implemented in Objective-C/Swift (iOS) or Java/Kotlin (Android).

Module TypeScript Support Notes
Native Module Interface declare module 'react-native' Define native modules in TypeScript using declaration files
Method Signatures method(param: string): Promise<boolean> Define return types and parameter types for native methods

Example: Typing a Native Module Method

declare module 'react-native' {
  interface NativeModule {
    isDeviceSecure(): Promise<boolean>;
  }
}

const NativeModule: NativeModule = require('react-native').NativeModules.NativeModule;

export const isDeviceSecure = (): Promise<boolean> => {
  return NativeModule.isDeviceSecure();
};
Note: Ensure that your native module's method signatures match the TypeScript definitions to avoid runtime errors.
Warning: Type definitions for native modules must be updated whenever the native implementation changes to maintain type safety.

8. Using Utility Types

Utility Type Description Example
Partial<T> Makes all properties in T optional interface User { name: string; age: number; }<br>type PartialUser = Partial<User>; // { name?: string; age?: number; }
Required<T> Makes all properties in T required interface User { name?: string; age?: number; }<br>type RequiredUser = Required<User>; // { name: string; age: number; }
Readonly<T> Makes all properties in T readonly interface User { name: string; age: number; }<br>type ReadonlyUser = Readonly<User>; // { readonly name: string; readonly age: number; }
Pick<T, K> Constructs a type by picking the set of properties K from T interface User { name: string; age: number; email: string; }<br>type UserEmail = Pick<User, 'email'>; // { email: string; }
Omit<T, K> Constructs a type by picking all properties from T except K interface User { name: string; age: number; email: string; }<br>type UserWithoutEmail = Omit<User, 'email'>; // { name: string; age: number; }
Record<K, T> Constructs a type with a set of properties K of type T type UserMap = Record<'admin' | 'user', string>; // { admin: string; user: string; }
Exclude<T, U> Constructs a type by excluding from T those types that are assignable to U type Union = 'a' | 'b' | 'c';<br>type Excluded = Exclude<Union, 'a'>; // 'b' | 'c'
Extract<T, U> Constructs a type by extracting from T those types that are assignable to U type Union = 'a' | 'b' | 'c';<br>type Extracted = Extract<Union, 'a' | 'b'>; // 'a' | 'b'
NonNullable<T> Constructs a type by excluding null and undefined from T type NullableString = string | null | undefined;<br>type NonNullableString = NonNullable<NullableString>; // string
Parameters<T> Extracts the parameter types from a function type type Fn = (a: number, b: string) => boolean;<br>type ParamTypes = Parameters<Fn>; // [number, string]
ReturnType<T> Extracts the return type from a function type type Fn = (a: number, b: string) => boolean;<br>type ReturnTypes = ReturnType<Fn>; // boolean

Example: Using Pick to create a form type

interface User {<br>  id: number;<br>  name: string;<br>  email: string;<br>  password: string;<br>}<br><br>// Create a form type with only name and email<br>type UserForm = Pick<User, 'name' | 'email'>;

Example: Using Partial to make a type optional

interface User {<br>  name: string;<br>  age: number;<br>}<br><br>// Make all properties optional<br>type PartialUser = Partial<User>;
Note: These utility types are part of the built-in TypeScript library and can be used in any TypeScript project.

9. Implementing Strict Type Checking

Example: Enabling Strict Mode in tsconfig.json

{
  "compilerOptions": {
    "strict": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictBindCallApply": true,
    "strictPropertyInitialization": true,
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true
  }
}
Option Description Default
strict Enables all strict type checking options false
strictNullChecks Ensures null and undefined are handled explicitly false
strictFunctionTypes Checks function parameter types in strict mode false
strictBindCallApply Checks types for bind, call, and apply methods false
strictPropertyInitialization Ensures class properties are initialized in constructor false
noImplicitAny Reports error when inferring any type false
noImplicitReturns Ensures all code paths return a value false
noImplicitThis Reports error when this has implicit any type false
noUnusedLocals Reports error for unused local variables false
noUnusedParameters Reports error for unused function parameters false
Note: Enabling strict mode helps catch type-related bugs early in development.
Warning: Enabling strict mode might require updating existing code to handle type errors.

10. Handling Third-Party Types

Example: Using @types Package

npm install --save-dev @types/react-native
Package Type Definition Notes
@types/react-native react-native Official type definitions for React Native
@types/react react Type definitions for React core
Note: Ensure that type definitions match the version of React Native you are using.

Example: Declaring Custom Types

declare module 'react-native' {
  interface MyComponentProps {
    title: string;
    onPress?: () => void;
  }
}

Type Definition Sources

  • DefinitelyTyped: Community-maintained type definitions
  • Built-in: Types included in the library itself
  • Custom: Manually declared types for third-party libraries

Was this article helpful?