0

I have written this package (https://www.npmjs.com/package/react-wise-router) which I use for some of my projects, and decided to upgrade to using TS. The problem is that some of the props I pass in are all grabbed as rest props which then is passed to another child component. My component's prop types:

interface WiseRouterProps {
  component: React.ComponentType<any> | null | undefined,
  needsAuthentication: boolean | undefined,
  needsAuthorisation: boolean | undefined,
  isAuthenticated: boolean | undefined,
  isAuthorised: boolean | undefined,
  userPermissions: string[] | undefined,
  routePermissions: string[] | undefined,
  redirectTo: string | null | undefined,
  defaultRedirect: string | '/' | undefined,
  fallback: null | Fallback | undefined,
  debug: boolean | undefined,
  passRouteProps: boolean | string[] | undefined,
  rest: [] | null | undefined;
}

If I pass in a prop such as exact or path which are not used by my component, and are only passed to a child component as rest, I get an error such as:

Type '{ exact: boolean; path: string; key: string; isAuthenticated: any; needsAuthentication: boolean; needsAuthorisation: boolean; routePermissions: never[]; userPermissions: any; passRouteProps: boolean; ... 4 more ...; debug: true; }' is not assignable to type 'IntrinsicAttributes & WiseRouterProps & { children?: ReactNode; }'.
Property 'exact' does not exist on type 'IntrinsicAttributes & WiseRouterProps & { children?: ReactNode; }'.

Which I think is logical, but I am grabbing every prop that is defined in my interface, and leave everything else as rest.

This is how I am grabbing my props including rest props:

const WiseRouter: React.FC<WiseRouterProps> = (props) => {

  const { component: Component = null, isAuthenticated, isAuthorised, needsAuthentication,
    needsAuthorisation, routePermissions,
    userPermissions, redirectTo,
    defaultRedirect = '/', fallback,
    debug = false, passRouteProps = false, ...rest } = props;
    //...
 }

I have been having this issue for some time now, and I would appreciate if someone could explain this to me. Thank you!

Edit: added code

import { BrowserRouter as Router, Switch, Redirect } from "react-router-dom";

// Routes is an array of objects defining each route
<Router>
    <Switch>
        {Routes.map(route => (
            <WiseRouter
                exact={true}
                path={route.path}
                key={route.name}
                isAuthenticated={isAuthenticated}
                needsAuthentication={route.needsAuthentication}
                needsAuthorisation={route.needsAuthorisation}
                routePermissions={route.permissions}
                userPermissions={permissions}
                passRouteProps={!!route.passRouteProps}
                redirectTo={route.redirectTo}
                defaultRedirect='/'
                component={route.component}
                fallback={route.fallback}
                debug={true} />
        ))}
        <Redirect from="*" to="/http-status/404" />
    </Switch>
</Router>
6
  • Where are you rendering this WiseRouter ? . Please add that code as well . As the error states you are missing a prop called 'exact' Commented Aug 27, 2021 at 15:18
  • @Shyam thank you for your response! it is rendered inside the Switch component from react-router-dom. Commented Aug 27, 2021 at 15:31
  • Can you add that code as well ? Looks like you are using the wrapper on top of Route component . React router dom should have a typing for Route component . So that in your code you can just do type WiseRouteProps = RouteProps & YourCustomProps Commented Aug 27, 2021 at 15:32
  • @Shyam Yes, correct. I have added the code :) Commented Aug 27, 2021 at 15:44
  • 1
    Shouldn't your component be called WiseRoute instead of WiseRouter? It's not actually routing, is it? Also, the parameters you cannot pass, are you passing them to the component prop? Do you know the types of routes you have? Because then you could do something similar as I have described in my answer. Commented Aug 27, 2021 at 15:54

1 Answer 1

1

You can simply avoid this issue and pass everything by extening Record<string, unknown>:

interface WiseRouterProps extends Record<string, unknown> {
    component: React.ComponentType<any> | null | undefined,
    // [...]
};

This means you will lose typechecking though.

So I would reccomend creating a specific routes property, and use it as generic so you don't loose that. I've assumed a lot and cobbled something together (even if it's not what you asked it was quite fun :D):

interface MainProps {
  mainInfo: string;
}

const Main: FC<MainProps> = props => <p>props.mainInfo</p>;

interface AboutProps {
  aboutInfo: string;
}

const About: FC<AboutProps> = props => <p>props.aboutInfo</p>;

interface Routes {
  [route: string]: ComponentType;
}

const routes = {
  main: Main,
  about: About
};

interface RouterProps<T extends Routes> {
  routes: T;
  // [...]
  needsAuthentication: boolean | undefined;
  // [...]
}

class Router<T extends Routes> extends Component<RouterProps<T>> {
  public navigate<Key extends keyof T>(
    key: Key,
    props: ComponentProps<T[Key]>
  ): void {
    // fancy pants navigate magic
  }
}

function App() {
  const routerRef = useRef<Router<typeof routes>>(null);

  // Works:
  routerRef.current.navigate('about', { aboutInfo: '' });
  // Does not work:
  routerRef.current.navigate('main', { aboutInfo: '' });

  return (
    <div>
      <Router ref={routerRef} routes={routes} needsAuthentication={false} />
    </div>
  );
}

Also, by doing needsAuthentication: boolean | undefined, you simply tell typescript that it bight be a bool or undefined. You will still need to provide needsAuthentication={undefined}. If you use needsAuthentication?: boolean you may actually omit the prop.

Check it out here: https://stackblitz.com/edit/react-ts-s2q4uc?file=index.tsx

Sign up to request clarification or add additional context in comments.

7 Comments

Ah, after you added that new piece of code I see that this is not what you were talking about... welp
Haha, your piece of code has been very eye opening as TS is not yet my strong assets. Do you think I could for now just somehow have the errors ignored for such props?
@MurphyAdam I don't think that's the best option. Are you defining Route? Because I'm currently working on getting it to work for something like that. (I'm also not the best as TS, just have a little experience :D)
I agree it is not the best..or a good option as it renders typechecking's goal invalid. I just want to get into the actual functionality and have this take care of in the development of the package above. You can have a look at the full code of the package on GitHub github.com/MurphyAdam/react-wise-router/blob/master/src/…
Well consider the option that I've posted at the very top of the answer, this will allow you to pass pretty much anythign.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.