Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(typescript): Fix incorrect type generated for glob route parameters | Add type tests #6364

Merged
merged 26 commits into from
Sep 16, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3330c73
Move route parser to inside rwjsRouter
dac09 Sep 6, 2022
09e462e
Simple tsdlite setup with jest
dac09 Sep 7, 2022
6c86058
Add tsd tests for routeParamsTypes
dac09 Sep 7, 2022
73d5906
Don't use caret
dac09 Sep 8, 2022
b98bba6
Dedupe yarn
dac09 Sep 8, 2022
db82751
Merge branch 'main' of github.com:redwoodjs/redwood into try/tsd-tests
dac09 Sep 9, 2022
7ca4f78
Break up RouteParser, add aditional tests
dac09 Sep 9, 2022
fbd69a0
Fix glob type |
dac09 Sep 9, 2022
e671ab2
Comment
dac09 Sep 9, 2022
d870521
Merge branch 'main' into try/tsd-tests
dac09 Sep 9, 2022
684f7c0
Rename one of the types
dac09 Sep 9, 2022
806d120
Merge main
dac09 Sep 12, 2022
50a7671
Use a ts file for types instead,
dac09 Sep 12, 2022
1845e62
Update packages/router/src/routeParamsTypes.ts
dac09 Sep 12, 2022
6fdeae5
Add extra tsconfig to typetests |
dac09 Sep 12, 2022
88edc39
Update packages/router/src/__typetests__/routeParamsTypes.test.ts
dac09 Sep 12, 2022
d860055
Merge branch 'main' into try/tsd-tests
dac09 Sep 13, 2022
061bcc8
Add a couple of more router matchPath tests
Tobbe Sep 15, 2022
8706a05
Merge branch 'main' into try/tsd-tests
dac09 Sep 15, 2022
e68fb7f
Add more tests
dac09 Sep 15, 2022
64758f6
Fix the strange edge cases
dac09 Sep 15, 2022
c1d424f
Use expectType in a different way for more accurate tests
dac09 Sep 15, 2022
9e3f586
Add FAQ to tests
dac09 Sep 15, 2022
37e1c79
tweak messages
dac09 Sep 15, 2022
7359f8c
Merge branch 'main' into try/tsd-tests
Tobbe Sep 16, 2022
b80f2d2
Merge branch 'main' into try/tsd-tests
dac09 Sep 16, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"@testing-library/react": "12.1.5",
"@testing-library/react-hooks": "8.0.1",
"@testing-library/user-event": "14.4.3",
"@tsd/typescript": "4.8.2",
"@types/babel__generator": "7.6.4",
"@types/fs-extra": "9.0.13",
"@types/jest": "29.0.0",
Expand All @@ -81,6 +82,7 @@
"fs-extra": "10.1.0",
"is-port-reachable": "3.1.0",
"jest": "29.0.1",
"jest-runner-tsd": "4.0.0",
"jscodeshift": "0.13.1",
"lerna": "5.4.3",
"lodash.template": "4.5.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
import '@redwoodjs/router'
import { RouteParams, QueryParams } from '@redwoodjs/router'
import { A } from 'ts-toolbelt'



type RouteParams<Route> = ${"Route extends `${string}/${infer Rest}`"}
? A.Compute<ParsedParams<Rest>>
: {}

type QueryParams = Record<string | number, string | number | boolean>

declare module '@redwoodjs/router' {
interface AvailableRoutes {
// Only "<Route />" components with a "name" and "path" prop will be populated here.
Expand All @@ -20,39 +13,3 @@ ${routes.map(
}
}

type ParamType<match> = match extends 'Int'
? number
: match extends 'Boolean'
? boolean
: match extends 'Float'
? number
: string

// Path string parser for Redwood Routes
type ParsedParams<PartialRoute> =
// {a:Int}/[...moar]
${"PartialRoute extends `{${infer Param}:${infer Match}}/${infer Rest}`"}
? // check for greedy match e.g. {b}/{c:Int}
// Param = b}/{c, Rest2 = {c, Match = Int so we reconstruct the old one {c + : + Int + }
${"Param extends `${infer Param2}}/${infer Rest2}`"}
? { [ParamName in Param2]: string } &
${"ParsedParams<`${Rest2}:${Match}}`> &"}
${"ParsedParams<`${Rest}`>"}
${": { [Entry in Param]: ParamType<Match> } & ParsedParams<`${Rest}`>"}
: // has type, but at the end e.g. {d:Int}
${"PartialRoute extends `{${infer Param}:${infer Match}}`"}
? // Greedy match order 2
${"Param extends `${infer Param2}}/${infer Rest2}`"}
? { [ParamName in Param2]: string } &
${"ParsedParams<`${Rest2}:${Match}}`>"}
: { [Entry in Param]: ParamType<Match> }
: // no type, but has stuff after it, e.g. {c}/{d}
${"PartialRoute extends `{${infer Param}}/${infer Rest}`"}
${"? { [ParamName in Param]: string } & ParsedParams<`${Rest}`>"}
: // last one with no type e.g. {d}
${"PartialRoute extends `{${infer Param}}`"}
? { [ParamName in Param]: string }
: // if theres a non param
${"PartialRoute extends `${string}/${infer Rest}`"}
${"? ParsedParams<`${Rest}`>"}
: {}
18 changes: 16 additions & 2 deletions packages/router/jest.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
/** @type {import('@jest/types').Config.InitialOptions} */
module.exports = {
setupFilesAfterEnv: ['./jest.setup.js'],
testEnvironment: 'jest-environment-jsdom',
projects: [
{
displayName: 'code',
setupFilesAfterEnv: ['./jest.setup.js'],
testEnvironment: 'jest-environment-jsdom',
testMatch: ['**/*.test.+(ts|tsx|js)', '!**/__typetests__/*.ts'],
},
{
displayName: {
color: 'blue',
name: 'types',
},
runner: 'jest-runner-tsd',
testMatch: ['**/__typetests__/*.test.ts'],
},
],
}
41 changes: 41 additions & 0 deletions packages/router/src/__typetests__/routeParamsTypes.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { expectType } from 'tsd-lite'

import { RouteParams } from '@redwoodjs/router'

test('Single parameters', () => {
const singleParameters: RouteParams<'bazinga/{id:Int}'> = {
id: 1,
}

expectType<{ id: number }>(singleParameters)
})

test('Route string with no types defaults to string', () => {
const singleParameters: RouteParams<'/blog/{year}/{month}/{day}/{slug}'> = {
year: '2020',
month: '01',
day: '01',
slug: 'hello-world',
}

expectType<{ year: string; month: string; day: string; slug: string }>(
singleParameters
)
})

test('Custom param types', () => {
const customParams: RouteParams<'/post/{name:slug}'> = {
name: 'hello-world-slug',
}

expectType<{ name: string }>(customParams)
})

test('Glob route params', () => {
dac09 marked this conversation as resolved.
Show resolved Hide resolved
const globRoutes: RouteParams<'/from/{fromDate...}/to/{toDate...}'> = {
fromDate: '2021/11/03',
toDate: '2021/11/17',
}

expectType<{ fromDate: string; toDate: string }>(globRoutes)
})
2 changes: 2 additions & 0 deletions packages/router/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,5 @@ export interface AvailableRoutes {
}

export { SkipNavLink, SkipNavContent } from '@reach/skip-nav'

export * from './routeParserTypes'
44 changes: 44 additions & 0 deletions packages/router/src/routeParserTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { A } from 'ts-toolbelt'

type GenericParams = Record<string | number, string | number | boolean>

export type QueryParams = GenericParams

export type RouteParams<Route> = Route extends `${string}/${infer Rest}`
? A.Compute<ParsedParams<Rest>>
: GenericParams

export type ParamType<match> = match extends 'Int'
? number
: match extends 'Boolean'
? boolean
: match extends 'Float'
? number
: string

// Path string parser for Redwood Routes
type ParsedParams<PartialRoute> =
// {a:Int}/[...moar]
PartialRoute extends `{${infer Param}:${infer Match}}/${infer Rest}`
? // check for greedy match e.g. {b}/{c:Int}
// Param = b}/{c, Rest2 = {c, Match = Int so we reconstruct the old one {c + : + Int + }
Param extends `${infer Param2}}/${infer Rest2}`
? { [ParamName in Param2]: string } & ParsedParams<`${Rest2}:${Match}}`> &
ParsedParams<`${Rest}`>
: { [Entry in Param]: ParamType<Match> } & ParsedParams<`${Rest}`>
: // has type, but at the end e.g. {d:Int}
PartialRoute extends `{${infer Param}:${infer Match}}`
? // Greedy match order 2
Param extends `${infer Param2}}/${infer Rest2}`
? { [ParamName in Param2]: string } & ParsedParams<`${Rest2}:${Match}}`>
: { [Entry in Param]: ParamType<Match> }
: // no type, but has stuff after it, e.g. {c}/{d}
PartialRoute extends `{${infer Param}}/${infer Rest}`
? { [ParamName in Param]: string } & ParsedParams<`${Rest}`>
: // last one with no type e.g. {d}
PartialRoute extends `{${infer Param}}`
? { [ParamName in Param]: string }
: // if theres a non param
PartialRoute extends `${string}/${infer Rest}`
? ParsedParams<`${Rest}`>
: Record<string | number, any>
73 changes: 72 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ __metadata:
languageName: node
linkType: hard

"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.18.6, @babel/code-frame@npm:^7.5.5, @babel/code-frame@npm:^7.8.3":
"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.15.8, @babel/code-frame@npm:^7.18.6, @babel/code-frame@npm:^7.5.5, @babel/code-frame@npm:^7.8.3":
version: 7.18.6
resolution: "@babel/code-frame@npm:7.18.6"
dependencies:
Expand Down Expand Up @@ -8272,6 +8272,13 @@ __metadata:
languageName: node
linkType: hard

"@tsd/typescript@npm:4.8.2":
version: 4.8.2
resolution: "@tsd/typescript@npm:4.8.2"
checksum: f77f252757ec658d730e7b58cba1700e53593e6ef35da92b90f432c0efb536deba9200b3bf4fd715d9cee47e0d322f566eb637dda749f3c61f1e7d9b2435e244
languageName: node
linkType: hard

"@types/aria-query@npm:^4.2.0":
version: 4.2.2
resolution: "@types/aria-query@npm:4.2.2"
Expand Down Expand Up @@ -13575,6 +13582,27 @@ __metadata:
languageName: node
linkType: hard

"create-jest-runner@npm:^0.12.0":
version: 0.12.0
resolution: "create-jest-runner@npm:0.12.0"
dependencies:
chalk: ^4.1.0
jest-worker: ^29.0.0
throat: ^6.0.1
peerDependencies:
"@jest/test-result": ^28.0.0 || ^29.0.0
jest-runner: ^28.0.0 || ^29.0.0
peerDependenciesMeta:
"@jest/test-result":
optional: true
jest-runner:
optional: true
bin:
create-jest-runner: generator/index.js
checksum: 8f808ed045666a83a25bb38080a888b3b9d1a3adcadfb1a51fdc622df6db185c1e4d62d5a00670383082e0a0b5affee03bc09d9082ee9125de8234201766e6c9
languageName: node
linkType: hard

"create-redwood-app@workspace:packages/create-redwood-app":
version: 0.0.0-use.local
resolution: "create-redwood-app@workspace:packages/create-redwood-app"
Expand Down Expand Up @@ -20223,6 +20251,20 @@ __metadata:
languageName: node
linkType: hard

"jest-runner-tsd@npm:4.0.0":
version: 4.0.0
resolution: "jest-runner-tsd@npm:4.0.0"
dependencies:
"@babel/code-frame": ^7.15.8
chalk: ^4.1.2
create-jest-runner: ^0.12.0
tsd-lite: ^0.6.0
peerDependencies:
"@tsd/typescript": ^3.8.3 || ^4.0.7
checksum: 4c64092881d29575589ad97ce3cf849d430a132319607fd6b8e172d45c231dcb67e2d1f4c4ca74e567c651421a30438c41077a452a3acfb59a27f7b34b1e47ac
languageName: node
linkType: hard

"jest-runner@npm:^29.0.1":
version: 29.0.1
resolution: "jest-runner@npm:29.0.1"
Expand Down Expand Up @@ -20451,6 +20493,17 @@ __metadata:
languageName: node
linkType: hard

"jest-worker@npm:^29.0.0":
version: 29.0.2
resolution: "jest-worker@npm:29.0.2"
dependencies:
"@types/node": "*"
merge-stream: ^2.0.0
supports-color: ^8.0.0
checksum: 64d32fe918e1f70294e3b7a2f5e873412c2809efaa8c960face611d0cce71324a30563484d20e906bcfbf872eabd0ea8f1c0e28f6678e9448ae1730064e1c811
languageName: node
linkType: hard

"jest-worker@npm:^29.0.1":
version: 29.0.1
resolution: "jest-worker@npm:29.0.1"
Expand Down Expand Up @@ -27115,6 +27168,7 @@ __metadata:
"@testing-library/react": 12.1.5
"@testing-library/react-hooks": 8.0.1
"@testing-library/user-event": 14.4.3
"@tsd/typescript": 4.8.2
"@types/babel__generator": 7.6.4
"@types/fs-extra": 9.0.13
"@types/jest": 29.0.0
Expand All @@ -27136,6 +27190,7 @@ __metadata:
fs-extra: 10.1.0
is-port-reachable: 3.1.0
jest: 29.0.1
jest-runner-tsd: 4.0.0
jscodeshift: 0.13.1
lerna: 5.4.3
lodash.template: 4.5.0
Expand Down Expand Up @@ -29332,6 +29387,13 @@ __metadata:
languageName: node
linkType: hard

"throat@npm:^6.0.1":
version: 6.0.1
resolution: "throat@npm:6.0.1"
checksum: 60a42d762a35d21ac71abd9eb4026b665fbbbf6ddd7bcbdcacc3c3b20f7b99f41939afedf9fe3273611f1b7c003ee98ac4dc94aa5edd1a6dc2a49985ad2545e1
languageName: node
linkType: hard

"throttle-debounce@npm:^2.1.0":
version: 2.3.0
resolution: "throttle-debounce@npm:2.3.0"
Expand Down Expand Up @@ -29790,6 +29852,15 @@ __metadata:
languageName: node
linkType: hard

"tsd-lite@npm:^0.6.0":
version: 0.6.0
resolution: "tsd-lite@npm:0.6.0"
peerDependencies:
"@tsd/typescript": ^3.8.3 || ^4.0.7
checksum: f8d66238d50c764c7afb93a355e8c437b853d0a66ab6dedf55c08b63614619d8a94aaab19c4c4b0a183b76c1bad088fccdc812778f574e02fd62206cc4b8ec30
languageName: node
linkType: hard

"tslib@npm:2.3.1, tslib@npm:~2.3.0":
version: 2.3.1
resolution: "tslib@npm:2.3.1"
Expand Down