typed-route
v1.0.0
Published
Type safe routes
Downloads
3
Maintainers
Readme
Quick example:
// constants/routes.ts
import { typedRoute, routeMap } from 'typed-route';
export const routes = {
list: '/list',
item: typedRoute<'id'>('/item/:id'),
form: routeMap(typedRoute<'id'>('/form/:id'), {
detail: '/detail',
subForm: typedRoute<'sub'>('/:sub')
})
};
// routes.tsx
import { routes } from './constants/routes';
const Routes = () => (
<Router>
<Route exact path={routes.list} component={ListPage} />
<Route exact path={routes.item} component={ItemPage} />
<Route exact path={routes.form.detail} component={FormDetailPage} />
<Route exact path={routes.form.subForm} component={SubFormPage} />
</Router>
);
// pages/sub-form.tsx
import { InferTypedRoute } from 'typed-route';
import { RouteComponentProps } from 'react-router-dom';
import { routes } from '../constants/routes';
type SubFormProps = RouteComponentProps<InferTypedRoute<typeof routes.form.subForm>>;
const SubFormPage: React.FC<SubFormProps> = ({ match }) => (
<div>
Subform params:
{ match.params.id }
{ match.params.sub }
</div>
);
// navigation.tsx
import { reverseUrl } from 'typed-route';
import { routes } from './constants/routes';
const Navigation = () => (
<div>
<Link to={reverseUrl(routes.item, { id: 1 })}>Link to item</Link>
<Link to={reverseUrl(routes.form.subForm, { id: 2, sub: 'test' })}>Link to sub form</Link>
{/* TS Error: Property 'id' is missing */}
<Link to={reverseUrl(routes.form.subForm, { sub: 'test' })}>Error link</Link>
</div>
);
Quick Start
Requirements
- npm or Yarn
- Node.js 10.0.0 or higher
- Typescript 3.5.0 or higher
Installation
$ npm install typed-route
If you are using Yarn, use the following command.
$ yarn add typed-route
Usage
typedRoute
typedRoute
- generic function creates string with saved context
typedRoute<'name'>('/:name')
- creates a string route with one param { name: string }
typedRoute<'id' | 'name'>('/:id/:name')
- creates a string route with a few params { id: string, name: string }
typedRoute<'id', 'name'>('/:id/:name?')
- you can add optional params { id: string, name?: string }
routeMap
Creates a route map merging all routes string and contexts
let routes;
routes = routeMap('/base', {
page1: '/page1',
page2: routeMap('/page2', {
info: '/info',
form: '/form'
})
});
console.log(routes);
/* {
index: '/base',
page1: '/base/page1',
page2: {
index: '/base/page2',
info: '/base/page2/info',
form: '/base/page2/form'
}
} */
reverseUrl
Type safe function generates an url using typed route and route params
reverseUrl(
typedRoute('/item'),
); // item
reverseUrl(
typedRoute<'id'>('/item/:id'),
{ id: 1 }
); // item/1
reverseUrl(
typedRoute<'id'>('/item/:id'),
); // TS Error
reverseUrl(
typedRoute<'id'>('/item/:id'),
{}
); // TS Error: Property 'id' is missing
reverseUrl(
typedRoute<'id' | 'optional'>('/item/:id/:second'),
{ id: 1, second: 2 }
); // item/1/2
reverseUrl(
typedRoute<'id', 'optional'>('/item/:id/:optional?'),
{ id: 1 }
); // item/1
reverseUrl(
typedRoute<'id', 'optional'>('/item/:id/:optional?'),
{ id: 1, optional: 'test' }
); // item/1/test
InferTypedRoute
Infer params from types route
let route = typedRoute<'id'>('/item/:id');
type I1 = InferTypedRoute<typeof route>;
// { id: string }
route = typedRoute<'id' | 'second'>('/item/:id/:second');
type I2 = InferTypedRoute<typeof route>;
// { id: string; second: string }
route = typedRoute<'id', 'optional'>('/item/:id/:optional?');
type I3 = InferTypedRoute<typeof route>;
// { id: string; optional?: string }
const routes = {
list: '/list',
form: routeMap(typedRoute<'id'>('/form/:id'), {
subForm: routeMap(typedRoute<'sub'>('/:sub'), {
detail: '/detail',
})
})
};
type I5 = InferTypedRoute<typeof routes.form.subForm.detail>;
// { id: string; sub: string }
You can use it with react-router
type SubFormProps = RouteComponentProps<InferTypedRoute<typeof routes.form.subForm.detail>>;
const SubFormPage: React.FC<SubFormProps> = ({ match }) => (
<div>
Subform params:
{ match.params.id }
{ match.params.sub }
</div>
);
InferRouteMap
Generates route map type params
const routes = {
list: '/list',
form: routeMap(typedRoute<'id'>('/form/:id'), {
info: '/info',
subForm: routeMap(typedRoute<'sub'>('/:sub'), {
detail: '/detail',
})
})
};
type RoutesParams = InferRouteMap<typeof routes>;
/* {
list: object;
form: {
index: { id: string };
info: { id: string };
subForm: {
index: { id: string };
detail: { id: string; sub: string }
}
}
*/
And you can use it with react-router instead of InferTypedRoute
type SubFormProps = RouteComponentProps<RoutesParams['form']['subForm']['detail']>;
const SubFormPage: React.FC<SubFormProps> = ({ match }) => (
<div>
Subform params:
{ match.params.id }
{ match.params.sub }
</div>
);