Property 'location' does not exist on type 'Readonly<{}> - React Router and Typescript

Viewed 5327

I'm trying to create a protected route for authentication. I have a ProtectedRoute component as suggested in the docs, that looks like this:

interface ProtectedRouteProps {
  auth: AuthState;
}

const ProtectedRoute = ({
  children,
  auth,
  ...rest
}: RouteProps & ProtectedRouteProps): JSX.Element => {
    console.log(auth)
  return (
    <Route
      {...rest}
      render={(props: RouteProps) =>
        auth.isAuthenticated ? children : <Redirect to={{pathname: "/signin", state: "Please sign in" }} />
      }
    />
  );
};

export default ProtectedRoute;

I then use it, and I want to show the state ("please sign in") if someone is redirected to the signin page.

I have a wrapper component that deals with routing:

export interface IWrapper {
    auth: AuthState
}

class Wrapper extends Component<IWrapper> {
    constructor(props: IWrapper){
        super(props);
        console.log(props)
    }
  render() {

    return (
      <div>
        <BrowserRouter>
          <HeaderContainer />
          <Switch>
          <Route exact path="/">
              <Home />
          </Route>
          <Route exact path="/signup">
            <SignUpView />
          </Route>
          <ProtectedRoute auth={this.props.auth} path="/dashboard">
              <DashboardContainer />
          </ProtectedRoute>
          </Switch>
          <Route exact path="/" component={Home}></Route>
          <Route exact path="/signout" component={SignUpView}></Route>
          <Route exact path="/signin" component={SignIn}></Route>

        </BrowserRouter>
      </div>
    );
  }
}

const mapStateToProps = (state: AppState) => ({
    auth: state.auth
})

export default connect(mapStateToProps)(Wrapper)

Here's my signin page:

export default class SignIn<RouteComponentProps> extends Component {
    constructor(props: RouteComponentProps){
        super(props);
        console.log(props);
    }


    render() {
        return (
            <div>
                {this.props.location.state ? (
                    <h2>You were redirected</h2>
                ) : ("")}
                <SignInForm />
            </div>
        )
    }
}

The typescript compiler says that I can't do props.location because "Property 'location' does not exist on type 'Readonly<{}> & Readonly<{ children?: ReactNode; }>'."

I really don't understand what this means. In the type definitions for RouteComponentProps you have:

export interface RouteComponentProps<Params extends { [K in keyof Params]?: string } = {}, C extends StaticContext = StaticContext, S = H.LocationState> {
  history: H.History;
  location: H.Location<S>;
  match: match<Params>;
  staticContext?: C;
}

So what's the problem? I've tried using Redirect props, but that doesn't work either. I'm a n00b at Typescript so if anyone could help I'd appreciate it. Thanks.

1 Answers

Without having read all of your code, my guess is that you can just change SignIn class definition from

export default class SignIn<RouteComponentProps> extends Component { ... }

to

export default class SignIn extends Component<RouteComponentProps> { ... }

, then you should be good to go.

In general generic type parameters are annotated following the name of the class, which in your case are not needed. In the extends clause concrete type arguments for the component to be extended are set. If you just write extends Component and don't specify the props type via Component<RouteComponentProps>, the default type {} of Component is used and TS cannot find location property on a common object literal {}.

More infos on generic classes can be found here.

Related