The most suitable TypeScript type for a screen being utilized again in react-navigation v5

When it comes to typing screens under react-navigation v5, I usually follow a simple pattern:

// Params definition
type RouteParamsList = {
    Screen1: {
        paramA: number
    }
    Screen2: undefined
}

// Screen1

type Props = StackScreenProps<RouteParamsList, 'Screen1'>

export const Screen1: React.FC<Props> = ...

It works perfectly for most cases.

However, I'm facing a challenge when trying to reuse the Screen1 component for different navigators:

// Params definition
type RouteParamsListShop = {
    Screen1: {
        paramA: number
    }
    Screen2: undefined
    Screen3: undefined
}

type RouteParamsListFeatures = {
    Screen1: {
        paramA: number
    }
    Screen4: undefined
}

// Screen1

type Props = StackScreenProps<RouteParamsListShop, 'Screen1'> | StackScreenProps<RouteParamsListFeatures, 'Screen1'> // Attempted solution

export const Screen1: React.FC<Props> = ...

Although using a union type allows me to extract parameters from the route correctly, the navigation method navigate poses an issue:

This expression is not callable. Each member of the union type '/* Route info here */' has signatures, but none of those signatures are compatible with each other.ts(2349)

Is there a way to properly address this typing issue, or should I consider restructuring my navigation setup to have the screen associated with only one route? Alternatively, creating two wrappers for different navigation instances may be another option.

Answer №1

If the two interfaces are identical, you can utilize this method:

interface Screen1Parameters {
  parameterA: number;
}

type ShopRouteParamsList = {
    Screen1: Screen1Parameters;
    Screen2: undefined;
    Screen3: undefined;
}

type FeaturesRouteParamsList = {
    Screen1: Screen1Parameters;
    Screen4: undefined;
}

...

type Props = StackScreenProps<ShopRouteParamsList, 'Screen1'>;

I haven't tested it yet, but using a Union should function as well, as long as the two types match:

type Props = StackScreenProps<ShopRouteParamsList, 'Screen1'> | StackScreenProps<FeaturesRouteParamsList, 'Screen1'>

Answer №2

There is a deeper issue at hand here beyond simply typing the union correctly. If our navigation prop belongs to RouteParamsListShop, then we are able to navigate to Screen2. However, if our navigation prop is for RouteParamsListFeatures, then we cannot navigate to Screen2 since it is not defined on that stack. This leads to an inherent compatibility issue.

Are you only attempting to navigate from Screen1 to another screen that is declared on both stacks? If so, we can handle this with proper typing. If not, there may be a design flaw present.

Instead of using a union, what you really need is a shared subset that includes both the target screen and the current screen. By implementing some helper types, we can simplify this process.

type SharedKeys = keyof RouteParamsListShop & keyof RouteParamsListFeatures

// We pick from both to account for potential differences in params
type SharedParams = Pick<RouteParamsListShop, SharedKeys> & Pick<RouteParamsListFeatures, SharedKeys>

type Props = StackScreenProps<SharedParams, 'Screen1'>

This setup enables you to utilize navigation.navigate - but restricts navigation to screens defined in both stack types. In the provided example, there are no valid screens left to navigate to, but presumably, there will be applicable navigation targets in your real-world scenario.

Experience TypeScript Playground

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Perform an Angular HTTP request and await responses from multiple sources

I'm currently working with Angular 11 and looking to make an HTTP call to an API that will trigger a server-side process. Here's the code for the HTTP call: processData(data) { return this.httpClient.post('https://myurl/api/process&apos ...

Unexpected Null Object Error in TypeScript Function

Hi there! I'm new to TypeScript and encountered an 'Object may be null' error in a function. The function is meant to add two LinkedLists together, each representing numbers (with each digit as its own node), and return a new LinkedList. Can ...

Preventing Redundancy in Angular 2: Tips for Avoiding Duplicate Methods

Is there a way I can streamline my if/else statement to avoid code repetition in my header component? Take a look at the example below: export class HeaderMainComponent { logoAlt = 'We Craft beautiful websites'; // Logo alt and title texts @Vie ...

Tips for accessing and modifying local files in Angular 2

Is there a method in Angular 2 to access files from an absolute path? I have utilized the 'filesaver' library for file saving, storing the files locally in txt/json formats. For instance: let blob = new Blob([document.getElementById(&apos ...

import component dynamically from object in Next.js

Currently, I have a collection of components that I am aiming to dynamically import using next/dynamic. I'm curious if this is achievable. Here's the object in interest: // IconComponents.tsx import { Tick, Star } from 'components ...

Using rxjs for exponential backoff strategy

Exploring the Angular 7 documentation, I came across a practical example showcasing the usage of rxjs Observables to implement an exponential backoff strategy for an AJAX request: import { pipe, range, timer, zip } from 'rxjs'; import { ajax } f ...

Combining Vue2 with Typescript can sometimes result in a missing field in an object when assigning a Prop to

If I am using TypeScript version 4.1 and have a component structured like this. @Component export default class Test extends Vue { @Prop() private msg!: string; private testObj={ msg: this.msg, test: 123 } created(){ console.log(JSON. ...

The 'push' property is not found within the 'Control' type

I am attempting to create an array that contains arrays within it. This array is intended for a dynamic form functionality, where the user can add a new section and push the array of control fields to the main array. Angular2 then generates this dynamical ...

Styling with react-jss based on intricate conditionals

After experimenting with react-jss for a few weeks, I am faced with the challenge of styling components efficiently. With numerous conditionals in these components, I am striving to avoid creating an excess of new components that essentially share the same ...

What is causing the error message "Module '@reduxjs/toolkit' or its type declarations are not found" to appear?

Although I have a good understanding of React-Redux, I decided to delve into TypeScript for further practice. Following the recommended approach from the react-redux team, I created a new project using the TS template: "npx degit reduxjs/redux-templa ...

Learn the technique of passing dual onClick parameters to a single function in React

I am working on a function named Test where I need to pass two onClick references. const Test = ({ onConnect }:{ onConnect:any }, { onDisconnect }:{ onDisconnect:any }) => { return ( <div> <DrawDiagram /> <button onClick ...

Issue with ReactJS not applying classes based on conditions

I'm currently working on toggling a class on an element when a user clicks. However, I am facing an issue where my code only sets the class for a single element and does not refresh for subsequent clicks. Even though the set class is not being removed ...

Encountering a Nextjs hydration issue after switching languages

I am facing an issue with my Next.js project using version v12.2.4 and implementing internationalization with i18next. The project supports two languages: Italian and English. Strangely, when I switch to English language, the app throws errors, while every ...

Typescript can represent both optional and required generic union types

Purpose My goal is to establish an optional parameter unless a specific type is provided, in which case the parameter becomes mandatory. Desired Outcome I aim for the get method below to default to having an optional parameter. However, if a type TT is p ...

Iterate through an array containing objects that may have optional properties, ensuring to loop through the entire

I need help iterating through an array of objects with a specific interface structure: export interface Incident { ID: string; userName1?: string; userName2?: string; userPhoneNumber?: string; crashSeverity: number; crashTime: number; } Here ...

What is the best approach to incorporate a refresh feature for a data stream?

I need to implement a function that updates the current list of items$ and allows for awaiting refreshItems(). This is my working implementation: private readStream = new Subject<T[]>(); readStream$ = this.readStream.asObservable(); getItems = (): ...

The lack of blur and focus methods in the react-native TextInput element is a notable limitation

Struggling to manage focus between multiple textInput elements with no blur and focus method available. Spent the entire day dealing with this issue, trying every solution found on the web but none seem to solve it. Not sure if there's a problem in my ...

Is there a way for me to convert my (TypeScript Definition) .d.ts file into a (JavaScript) .js file?

It seems that there are plenty of guides available on converting a file from .js to .d.ts, but not the other way around. Specifically, I have some code in .d.ts format and I would like to convert it into JavaScript. Can anyone offer assistance with this t ...

steps for signing in to garmin account with javascript

Attempting to set up an Oauth1 login for Garmin using Angular2 and standard http calls, but encountering a pre-flight OPTIONS call error on the initial request to oauth/request_token path. It seems like CORS is not enabled or something similar. Has anyone ...

What are some strategies for preventing a child component from triggering a re-render of its parent component in React Native?

My child component is causing the parent component to re-render when it's clicked, which is not the desired behavior. I initially thought that removing all the setState() functions in the child component would prevent this, but then I realized that As ...