34
votes

I'm using typescript in my react-native project(expo).

The project uses react-navigation, so on my screens I can set navigationOptions and I have access to the prop navigation.

Now I'm trying to strongly type these so I get hints for what properties are available to set.

interface NavStateParams {
    someValue: string
}

interface Props extends NavigationScreenProps<NavStateParams> {
   color: string
}

class Screen extends React.Component<Props, any> {
    // This works fine
    static navigationOptions: NavigationStackScreenOptions = {
        title: 'ScreenTitle'
    }
    // Does not work
    static navigationOptions: NavigationStackScreenOptions = ({navigation, screenProps }) => ({
        title: navigation.state.params.someValue
    })
}

What would be the best way to handle react-navigation as props for components.

11

11 Answers

40
votes

Just add NavigationType to your Props, like this:

    import { StackNavigator, NavigationScreenProp } from 'react-navigation';

    export interface HomeScreenProps {
      navigation: NavigationScreenProp<any,any>
    };

    export class HomeScreen extends React.Component<HomeScreenProps, object> {

      render() {
        return (
          <View style={styles.container}>       
            <Button
              title="Go to Details"
              onPress={() => this.props.navigation.navigate('Details')}
            />
          </View>
        );
      }
    }
4
votes

I have same issue, and here's my solution:

import * as React from 'react'
import { NavigationScreenProps, NavigationStackScreenOptions } from 'react-navigation'

interface NavStateParams {
  someValue: string
}

// tslint:disable-next-line:no-any
type NavigationOptionsFn<TParams=any> = (props: NavigationScreenProps<TParams>) => NavigationStackScreenOptions

class Screen extends React.Component {
  // This should works fine
  static navigationOptions: NavigationOptionsFn<NavStateParams> = ({ navigation, screenProps }) => ({
    title: navigation.state.params.someValue
  })
}

You may want to declare NavigationOptionsFn<TParams> type into some d.ts file to make it work globally.

4
votes

This works:

static navigationOptions = ({ navigation }: NavigationScreenProps) => ({
  ...
})
4
votes

I think with react-navigation 5.X it's simpler now. Here is how to type hint navigation props passed to screens/components:

export default class Header extends React.Component<{
    navigation: StackNavigationHelpers;
}> {
...
}

Ps: Tested with these versions

"@react-navigation/native": "^5.2.3",
"@react-navigation/stack": "^5.3.1",
3
votes
 yarn add --dev @types/jest @types/react-navigation

import { NavigationScreenProps } from "react-navigation";

export interface ISignInProps extends NavigationScreenProps<{}> { userStore: IUserStore }

export class SignInScreen extends React.Component { .... }
2
votes
public static navigationOptions: NavigationScreenConfig<NavigationStackScreenOptions> = 
    ({navigation}) => ({/* Your options... */})
2
votes

In case anyone is still experiencing this issue while extending NavigationScreenProps so you can correctly type navigationOptions etc along with your own props:

interface Props extends NavigationScreenProps {
  someProp: string;
  anotherProp: string;
}

export const SomeGreatScreen: NavigationScreenComponent<NavigationParams, {}, Props> = ({
  someProp,
  anotherProp,
}) => {
...
};

Whereas NavigationScreenComponent<Props> resulted in type errors for the destructured properties { someProp, anotherProp }, not recognizing the extension to the props, NavigationScreenComponent<NavigationParams, {}, Props> did. This appears to be due to the need to send the extended props type as the third parameter:

  export type NavigationScreenComponent<
    Params = NavigationParams,
    Options = {},
    Props = {}
  > = React.ComponentType<NavigationScreenProps<Params, Options> & Props> & {
    navigationOptions?: NavigationScreenConfig<Options>;
  };

from react-navigation.d.ts

1
votes

Rather than describing manually all your navigation functions (e.g: navigate), in your Props' interface, you can directly extends NavigationScreenProps.

In my case it was mandatory to stop eslint from yielding an error.

import { StackNavigator, NavigationScreenProps } from 'react-navigation';

export interface HomeScreenProps extends NavigationScreenProps {
/* your custom props here */
};

export class HomeScreen extends React.Component<HomeScreenProps, object> {

  render() {
    return (
      <View style={styles.container}>       
        <Button
          title="Go to Details"
          onPress={() => this.props.navigation.navigate('Details')}
        />
      </View>
    );
  }
}
0
votes

This seems to work:

public static navigationOptions: NavigationScreenOptionsGetter<
  NavigationScreenOptions
> = (navigation, stateProps) => ({
  title: navigation.state.params.someValue,
});
0
votes

The does not work section contains an error if your tsconfig.json has "strictNullChecks": true. In this case, you have an error indeed, because in the line

navigation.state.params.someValue

params is optional. What you could do is to check if the value was passed inside and provide default one otherwise, like:

title: navigation.state.params && navigation.state.params.someValue || 'Default title'

0
votes

if you are passing navigation prop that is defined by

let navigation = useNavigation()

to a component, the best way of typing is:

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

navigation: NavigationProp<ParamListBase>