Skip to content

Commit 0486f02

Browse files
committed
move the type and add to README
1 parent b768c10 commit 0486f02

File tree

3 files changed

+54
-42
lines changed

3 files changed

+54
-42
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,16 @@ The difference between `/:id*` and `/:id/*` is that in the former, the `id` para
261261
- `/profile/:id/*`, with `/profile/123/abc`
262262
- `id` is `123`
263263

264+
You can narrow prop types for your routes using `RoutePropsForPath<path>`:
265+
```js
266+
import type { RoutePropsForPath } from 'preact-iso/router'
267+
268+
function User(props: RoutePropsForPath<'/user/:id'>) {
269+
props.user.id2 // type error
270+
props.user.id // no type error
271+
}
272+
```
273+
264274
### `useLocation`
265275

266276
A hook to work with the `LocationProvider` to access location context.

src/router.d.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,32 @@ type RoutableProps =
5959

6060
export type RouteProps<Props> = RoutableProps & { component: AnyComponent<Props> };
6161

62+
export type RoutePropsForPath<Path extends string> = Path extends '*'
63+
? { params: {}; rest: string }
64+
65+
: Path extends `:${infer placeholder}?/${infer rest}`
66+
? { [k in placeholder]?: string } & { params: RoutePropsForPath<rest>['params'] & { [k in placeholder]?: string } } & Omit<RoutePropsForPath<rest>, 'params'>
67+
68+
: Path extends `:${infer placeholder}/${infer rest}`
69+
? { [k in placeholder]: string } & { params: RoutePropsForPath<rest>['params'] & { [k in placeholder]: string } } & Omit<RoutePropsForPath<rest>, 'params'>
70+
71+
: Path extends `:${infer placeholder}?`
72+
? { [k in placeholder]?: string } & { params: { [k in placeholder]?: string } }
73+
74+
: Path extends `:${infer placeholder}*`
75+
? { [k in placeholder]?: string } & { params: { [k in placeholder]?: string } }
76+
77+
: Path extends `:${infer placeholder}+`
78+
? { [k in placeholder]: string } & { params: { [k in placeholder]: string } }
79+
80+
: Path extends `:${infer placeholder}`
81+
? { [k in placeholder]: string } & { params: { [k in placeholder]: string } }
82+
83+
: Path extends (`/${infer rest}` | `${infer _}/${infer rest}`)
84+
? RoutePropsForPath<rest>
85+
86+
: { params: {} };
87+
6288
export function Route<Props>(props: RouteProps<Props> & Partial<Props>): VNode;
6389

6490
declare module 'preact' {
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,7 @@
11
// Test this file by running:
2-
// npx tsc --noEmit test/node/pattern-match.test.ts
2+
// npx tsc --noEmit test/node/pattern-match.types.ts
33

4-
type RoutePropsNarrow<Re extends string> = Re extends '*'
5-
? { params: {}; rest: string }
6-
7-
: Re extends `:${infer placeholder}?/${infer rest}`
8-
? { [k in placeholder]?: string } & { params: RoutePropsNarrow<rest>['params'] & { [k in placeholder]?: string } } & Omit<RoutePropsNarrow<rest>, 'params'>
9-
10-
: Re extends `:${infer placeholder}/${infer rest}`
11-
? { [k in placeholder]: string } & { params: RoutePropsNarrow<rest>['params'] & { [k in placeholder]: string } } & Omit<RoutePropsNarrow<rest>, 'params'>
12-
13-
: Re extends `:${infer placeholder}?`
14-
? { [k in placeholder]?: string } & { params: { [k in placeholder]?: string } }
15-
16-
: Re extends `:${infer placeholder}*`
17-
? { [k in placeholder]?: string } & { params: { [k in placeholder]?: string } }
18-
19-
: Re extends `:${infer placeholder}+`
20-
? { [k in placeholder]: string } & { params: { [k in placeholder]: string } }
21-
22-
: Re extends `:${infer placeholder}`
23-
? { [k in placeholder]: string } & { params: { [k in placeholder]: string } }
24-
25-
: Re extends (`/${infer rest}` | `${infer _}/${infer rest}`)
26-
? RoutePropsNarrow<rest>
27-
28-
: { params: {} };
4+
import type { RoutePropsForPath } from '../../src/router.js';
295

306
// Test utils
317

@@ -36,92 +12,92 @@ type isWeakEqualsType<T, U> = T extends U ? true : false;
3612

3713
// Base route test
3814
const test1: isEqualsType<
39-
RoutePropsNarrow<'/'> ,
15+
RoutePropsForPath<'/'> ,
4016
{ params: {} }
4117
> = true;
4218

4319
const test1_1: isEqualsType<
44-
RoutePropsNarrow<'/'> ,
20+
RoutePropsForPath<'/'> ,
4521
{ arbitrary: {} }
4622
> = false;
4723

4824
// Param route test
4925
const test2: isEqualsType<
50-
RoutePropsNarrow<'/user/:id'> ,
26+
RoutePropsForPath<'/user/:id'> ,
5127
{ params: { id: string }, id: string }
5228
> = true;
5329

5430
const test2_weak: isWeakEqualsType<
55-
RoutePropsNarrow<'/user/:id'> ,
31+
RoutePropsForPath<'/user/:id'> ,
5632
{ params: { id: string } }
5733
> = true;
5834

5935
// Param rest segment test
6036
const test3: isEqualsType<
61-
RoutePropsNarrow<'/user/*'> ,
37+
RoutePropsForPath<'/user/*'> ,
6238
{ params: {}, rest: string }
6339
> = true;
6440

6541
const test3_1: isEqualsType<
66-
RoutePropsNarrow<'/*'> ,
42+
RoutePropsForPath<'/*'> ,
6743
{ params: {}, rest: string }
6844
> = true;
6945

7046
const test3_2: isEqualsType<
71-
RoutePropsNarrow<'*'> ,
47+
RoutePropsForPath<'*'> ,
7248
{ params: {}, rest: string }
7349
> = true;
7450

7551
// Param route with rest segment test
7652
const test4: isEqualsType<
77-
RoutePropsNarrow<'/user/:id/*'> ,
53+
RoutePropsForPath<'/user/:id/*'> ,
7854
{ params: { id: string }, id: string, rest: string }
7955
> = true;
8056

8157
// Optional param route test
8258
const test5: isEqualsType<
83-
RoutePropsNarrow<'/user/:id?'> ,
59+
RoutePropsForPath<'/user/:id?'> ,
8460
{ params: { id?: string }, id?: string }
8561
> = true;
8662

8763
// Optional rest param route "/:x*" test
8864
const test6: isEqualsType<
89-
RoutePropsNarrow<'/user/:id*'> ,
65+
RoutePropsForPath<'/user/:id*'> ,
9066
{ params: { id?: string }, id?: string }
9167
> = true;
9268

9369
// rest param should not be present
9470
const test6_error: isEqualsType<
95-
RoutePropsNarrow<'/user/:id*'> ,
71+
RoutePropsForPath<'/user/:id*'> ,
9672
{ params: { id: string }, rest: string }
9773
> = false;
9874

9975
// Rest param route "/:x+" test
10076
const test7: isEqualsType<
101-
RoutePropsNarrow<'/user/:id+'> ,
77+
RoutePropsForPath<'/user/:id+'> ,
10278
{ params: { id: string }, id: string }
10379
> = true;
10480

10581
// rest param should not be present
10682
const test7_error: isEqualsType<
107-
RoutePropsNarrow<'/user/:id+'>,
83+
RoutePropsForPath<'/user/:id+'>,
10884
{ params: { id: string }, id: string, rest: string }
10985
> = false;
11086

11187
// Handles leading/trailing slashes test
11288
const test8: isEqualsType<
113-
RoutePropsNarrow<'/about-late/:seg1/:seg2/'> ,
89+
RoutePropsForPath<'/about-late/:seg1/:seg2/'> ,
11490
{ params: { seg1: string; seg2: string }, seg1: string, seg2: string }
11591
> = true;
11692

11793
// Multiple params test (from overwrite properties test)
11894
const test9: isEqualsType<
119-
RoutePropsNarrow<'/:path/:query'> ,
95+
RoutePropsForPath<'/:path/:query'> ,
12096
{ params: { path: string; query: string }, path: string, query: string }
12197
> = true;
12298

12399
// Empty route test
124100
const test10: isEqualsType<
125-
RoutePropsNarrow<''> ,
101+
RoutePropsForPath<''> ,
126102
{ params: {} }
127103
> = true;

0 commit comments

Comments
 (0)