46

[I keep getting the error that says 'string' is not assignable to parameter of type 'never' in react native typescript and I don't know why. Can someone help me fix this bug.

Thank you in advance.

code picture

code snippet :

const loadReport = (id: string) => {
    setPostId(id);
    navigation.navigate('Report', {postId: id});
}

I get an underline under 'Report'.

128KB
  • 398
  • 3
  • 12
BattleVlog
  • 463
  • 1
  • 4
  • 5

9 Answers9

78

This is a weird issue that happens in RN > 0.65. My solution:

1.- Import:

{/* Depends of your Package (Stack or NativeStack...) */}
import { StackNavigationProp } from '@react-navigation/stack';
import { useNavigation } from '@react-navigation/core';

2.- Define types

export type RootStackParamList = {
  YourScreen: { id: number } | undefined;
};

3.- Assign useNavigation hook with type StackNavigationProp.

const navigation = useNavigation<StackNavigationProp<RootStackParamList>>();

4.- Use it! (Eye: with this type you can access to navigation object)

<TouchableOpacity
   // Use this when you pass a parameter (is optional)
   onPress={() => navigation.navigate('YourScreen', {id: 5})}>
</TouchableOpacity>

5.- Remember this notes:

  • Specifying undefined means that the route doesn't have params. A union type with undefined => AnyType | undefined means that params are optional.

  • The useNavigation const have special type and this final type takes 3 generics:

  • The param list object => RootStackParamList

  • The name of the screen route => RouteName

  • The ID of the navigator (optional) => NavigatorID

Obtained from: https://reactnavigation.org/docs/typescript/#annotating-usenavigation

Parterdev
  • 881
  • 1
  • 2
  • 6
  • And if I need 'YourScreen' is: ```export const SCREEN_NAME = { YourScreen: 'YourScreen' } ``` so there is one warning here: ```onPress={() => navigation.navigate(SCREEN_NAME.YourScreen, {id: 'smth'})}>``` Argument of type 'string' is not assignable to parameter of type 'keyof StackParamList'. – user3595795 Mar 24 '22 at 14:42
  • I've got an error: `Module '"@react-navigation/native-stack"' has no exported member 'StackNavigationProp'` – Nam Lee Aug 23 '22 at 22:39
  • @user3595795 I've got same problem – Nam Lee Aug 23 '22 at 22:40
  • 2
    for v6, I had to update the import to work it: `import { NativeStackNavigationProp } from '@react-navigation/native-stack';` – AbdulRehman Sep 07 '22 at 01:18
  • 1
    Hi world! Thousand apologies for the delay... I'm back with React Native (specially with navigation and the error that you've commented here) => The solution is super easy. SOLUTION: Change YourScreen: {param: type} ({id: string}) for Home: { id: number } | undefined => NOTE: undefined means that the route doesn't have params. FINALLY: Check the code update ;) – Parterdev Nov 10 '22 at 23:08
  • If using native stack v6 use `import { NavigationProp } from '@react-navigation/native'` – jessmzn Apr 13 '23 at 19:54
43

The only solution I found is to apply the type never on the string name.

const goToContent = () => {
    navigate("Content" as never, {} as never);
};

I'm not sure it's the best solution but it's work.

Michiel Pater
  • 22,377
  • 5
  • 43
  • 57
  • 2
    The OP doesn't specify which version of react-navigation they're using but some of the other solutions are more appropriate than this one, for react navigation 6 at least. – lee_mcmullen Dec 08 '21 at 09:48
  • 3
    Good for using once or twice, will create more problematic when to use at multiple places, – AbdulRehman Sep 07 '22 at 01:20
13

Simple solution is to add any to NativeStackNavigationProp type

const AppLink = ({
  text,
  screenName,
}: Link) => {

  // pass any to overload 
  const navigation = useNavigation<NativeStackNavigationProp<any>>();

  const handleNavigation = () => {
    navigation.navigate(screenName);
  };  
  
  return (
    <TouchableOpacity onPress={handleNavigation}>
      <Text>{text}</Text>
    </TouchableOpacity>
  );
};

export default AppLink;
11

I too was getting the following ts error when not annotating useNavigation and trying to call navigation.navigate('OtherJazzyScreen)`:

Argument of type 'string' is not assignable to parameter of type '{ key: string; params?: undefined; merge?: boolean | undefined; } | { name: never; key?: string | undefined; params: never; merge?: boolean | undefined; }'

The OP doesn't specify which version of react-navigation they're using but you can fix this globally in React Navigation 6 which means you then don't have to annotate useNavigation directly but you still get autocomplete and type checking.

Taken from the react-navigation blog in August 2021 (https://reactnavigation.org/blog/2021/08/14/react-navigation-6.0/#better-type-safety):

In React Navigation 6, you don’t need to annotate useNavigation to get autocompletion and type checking. This is possible by defining a type for the screens globally using declaration merging:

declare global {   
  namespace ReactNavigation {
    interface RootParamList {
      Home: undefined;
      Profile: { userId: string };
      NotFound: undefined;
    }   
  } 
 } 

You can read more about it in our TypeScript docs.

Another example from the React Navigation 6 docs, if you already have your params declared elsewhere:

// E.g. RootStackParamList.ts
export type RootStackParamList = {
  Home: undefined;
  Profile: { userId: string };
  NotFound: undefined;
};

// E.g. App.tsx
import { RootStackParamList } from 'path/to/RootStackParamList';
declare global {
  namespace ReactNavigation {
    interface RootParamList extends RootStackParamList {}
  }
}
lee_mcmullen
  • 2,801
  • 32
  • 39
7

Just use type any when calling the hook:

const navigation = useNavigation<any>();

This isn't the recommended way of doing things but it will make the code error go away.

ryanwaite28
  • 1,804
  • 2
  • 24
  • 40
3

I ran into this problem as well; in my case I was starting from the Ignite boilerplate. I solved it by using:

const navigation = useNavigation<NativeStackNavigationProp<AppStackParamList>>()

Where, in my case, AppStackParamList is defined in app/navigators/AppNavigator.tsx. There can be more than one Navigator and more than one ...StackParamList; you'll wanna use the proper param list for the navigator your component is in. By passing the param list (instead of any) to NativeStackNavigationProp the type system can infer the list of route names for suggesting in navigation.navigate() as well as other things relevant to your actual screen list.

TheZanke
  • 1,026
  • 8
  • 12
2

You could make a maintainable navigation prop.

  1. Make a type file.
// navigation.ts
import { DrawerNavigationProp } from "@react-navigation/drawer";
import { NavigationProp } from "@react-navigation/native";
import { StackNavigationProp } from "@react-navigation/stack";

type RootStackParamList = {
  // Your custom param type belongs here.
  Login: {
    id: string;
  },
  Profile: {}, 
}

type RootDrawerParamList = {
  Shopping: {},
  Chat: {},
}

export type RootNavigationProp = NavigationProp<
 StackNavigationProp<RootStackParamList>,
 DrawerNavigationProp<RootDrawerParamList>
>;
  1. Use it.
import { useNavigation } from "@react-navigation/native";
import { RootNavigationProp } from "@somewhere/navigation";

const App = () => {
  const navigation = useNavigation<RootNavigationProp>();

  function onPress () {
    navigation.navigate("Login", {id: "userID"});
  }
}
Hyeonmin
  • 135
  • 1
  • 4
0

this is the correct solution of the typing you are searching for.

import React from 'react';
import {Text, View, Button} from 'react-native';
import {StackNavigationProp} from '@react-navigation/stack';
import {ParamListBase, useNavigation} from '@react-navigation/native';

enum Url {
  Page1Screen = 'Page1Screen',
  Page2Screen = 'Page2Screen',
  Page3Screen = 'Page3Screen',
}

export const Page2Screen = () => {
  const navigation = useNavigation<StackNavigationProp<ParamListBase, Url>>();
  return (
    <View>
      <Text>page2</Text>
      <Button
        title="go to page 1"
        onPress={() => navigation.navigate('Page1Screen')}
      />
      <Button
        title="go to page 3"
        onPress={() => navigation.navigate('Page3Screen')}
      />
    </View>
  );
};
-1

I found this solution:

const route = ["path", { data }];
navigation?.navigate(...(route as never));