import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Route, BrowserRouter as Router, Link, match } from 'react-router-dom';
// define React components for multiple pages
class Home extends React.Component<any, any> {
render() {
return (
<div>
<div>HOME</div>
<div><Link to='/details/id123'>Goto Details</Link></div>
</div>);
}
}
interface DetailParams {
id: string;
}
interface DetailsProps {
required: string;
match?: match<DetailParams>;
}
class Details extends React.Component<DetailsProps, any> {
render() {
const match = this.props.match;
if (match) {
return (
<div>
<div>Details for {match.params.id}</div>
<Link to='/'>Goto Home</Link>
</div>
);
} else {
return (
<div>
<div>Error Will Robinson</div>
<Link to='/'>Goto Home</Link>
</div>
)
}
}
}
ReactDOM.render(
<Router>
<div>
<Route exact path="/" component={Home} />
<Route exact path="/details/:id" component={(props) => <Details required="some string" {...props} />} />
</div>
</Router>
, document.getElementById('root')
);
In order to preserve type safety for the Details
component which has a required property named required
, the <Route>
definition defines an anonymous function-based component which composes a new component of type <Details>
and specifying the required
property.
The spread operator is utilized to re-apply the props
passed to the anonymous function-based component onto the composed <Details>
component.
The match
property is defined as optional, since it's filled in dynamically by react-router
, we, unfortunately, cannot define it as required property. This means a type guard is required when accessing the values later.