{"_id":"static-path","_rev":"5-4dcfea3730e0fb2d0fd506750940302a","name":"static-path","dist-tags":{"latest":"0.0.4"},"versions":{"0.0.1":{"name":"static-path","version":"0.0.1","description":"Static-path uses TypeScript to prevent 404s and other path generation mistakes at compile time","main":"lib/index.js","types":"lib/index.d.ts","scripts":{"test":"ava","build":"npm-run-all format compile test lint","compile":"tsc","format":"prettier --write 'src/**/*.ts'","lint":"eslint --max-warnings 0 --ext '.js,.jsx,.ts,.tsx' src","prepare":"npm run build","prepublishOnly":"npm test && npm run lint"},"repository":{"type":"git","url":"git+https://github.com/garybernhardt/static-path.git"},"keywords":["path","url","link","404","pattern","routing","router"],"author":{"name":"Gary Bernhardt"},"license":"ISC","bugs":{"url":"https://github.com/garybernhardt/static-path/issues"},"homepage":"https://github.com/garybernhardt/static-path#readme","devDependencies":{"@typescript-eslint/eslint-plugin":"^4.29.1","@typescript-eslint/parser":"^4.29.1","ava":"^3.15.0","eslint":"^7.32.0","eslint-config-prettier":"^8.3.0","npm-run-all":"^4.1.5","prettier":"^2.3.2","typescript":"^4.3.5"},"gitHead":"3bb0ae9d308f58c7cdfdab58ff71c6e8a0d80b49","_id":"static-path@0.0.1","_nodeVersion":"14.17.2","_npmVersion":"6.14.13","dist":{"integrity":"sha512-wt59Mn5b148JmvlxQy1gVi45XuVuuFf69+Yc54FNBfrELpB5l+tOvFBKnUabIGBF7XVbppWUOzQfKBpAWN357Q==","shasum":"f08fb7334e4f0362083b17ffab035c6b2c106c7e","tarball":"https://registry.npmjs.org/static-path/-/static-path-0.0.1.tgz","fileCount":7,"unpackedSize":25992,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhFurfCRA9TVsSAnZWagAAhT8P/2/BAq9nO+DBGW3CwFD6\ncbQzfi+sweOrkHkEn7AJMq/MkJu/jZqvbnjWKC+oyDijkXM9/ICM8EaMdNat\n1OYbtSMEfbcZ66kv2KP6Ph5JQdboUZY+xrByqYQAgLgpjTdpBW5tgJCIKa1B\nGtowRS8X6SeUFjqDC4rUx8Mzt1izdZZixObuQ3AcP31l9rIdZtXCUrMacoWI\nF1mruJqeKD97ZYYaDFYkR7Evozn8N1NLerSt0QEQmwFDhHBv3WK9GTXj7Rnz\nh4bf40TU5S8qdT4TXo3bToeoB/xrC7OINLL3F6ogiqcE72hPNkGGgF3th8OH\nu6Pa9ZQsKYrtjPqSZtDghiCH5/rfi1/uGtdW89h7NgGbpH6Ec8ImJUoZzn92\n3fBgXJIPVDrLPg5IZGHMmJBohgVJtCMvVZrkelhhvSfZGTTpYfi0bvyMSQS3\nzf1IdATTvd5F0RCicrtNb8ufhREmJpX2zZ85Hkv/mu1bo440nmkpWgAR+cts\nL/OoW5ZvsLf0VWUQXUnGEuqVMz8/AlaGD9NOs1Ila0A4hvorEo0p4qp4Uwap\nUmINAHOu5t5+OQaWX7Wj7Iry9cBdeWVSrchDJUi8PVBNsGHHkWm3l6WBjsR8\nks6Z0piO4Us1TBI5gyQJujMZyRdnxpbxv5xISVkSKLtBYB+zb9I0/mtKpwoL\nJuXK\r\n=nN1l\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG/AavRjNS6UJe7HGwCi9g4KL04yTkJ1hFK18Ym2vgM9AiEA5DveFBdW0CR09wH+R+IGOlijZsjk0oYOLe+R4cnMPe8="}]},"_npmUser":{"name":"garybernhardt","email":"gary.bernhardt@gmail.com"},"directories":{},"maintainers":[{"name":"garybernhardt","email":"gary.bernhardt@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/static-path_0.0.1_1628891871112_0.5171298419541526"},"_hasShrinkwrap":false},"0.0.2":{"name":"static-path","version":"0.0.2","description":"Static-path uses TypeScript to prevent 404s and other path generation mistakes at compile time","main":"lib/index.js","types":"lib/index.d.ts","scripts":{"test":"ava","build":"npm-run-all format compile test lint","compile":"tsc","format":"prettier --write 'src/**/*.ts'","lint":"eslint --max-warnings 0 --ext '.js,.jsx,.ts,.tsx' src","prepare":"npm run build","prepublishOnly":"npm test && npm run lint"},"repository":{"type":"git","url":"git+https://github.com/garybernhardt/static-path.git"},"keywords":["path","url","link","404","pattern","routing","router"],"author":{"name":"Gary Bernhardt"},"license":"ISC","bugs":{"url":"https://github.com/garybernhardt/static-path/issues"},"homepage":"https://github.com/garybernhardt/static-path#readme","devDependencies":{"@typescript-eslint/eslint-plugin":"^4.29.1","@typescript-eslint/parser":"^4.29.1","ava":"^3.15.0","eslint":"^7.32.0","eslint-config-prettier":"^8.3.0","npm-run-all":"^4.1.5","prettier":"^2.3.2","typescript":"^4.3.5"},"gitHead":"0b8ae1db2799c28d53d7c73ec5a768cedae5e50b","_id":"static-path@0.0.2","_nodeVersion":"14.17.2","_npmVersion":"6.14.13","dist":{"integrity":"sha512-j4GkcyjlRClUpNRAoqh3b69lvuGn+5ogmhqYjWrAvHd/+MU52nv+y5giqKHtTRmsWevsHrXkIaG6J4/GlvJCbw==","shasum":"f34c2155b3cc32c4730b7d06e6fd075a5a47aed6","tarball":"https://registry.npmjs.org/static-path/-/static-path-0.0.2.tgz","fileCount":9,"unpackedSize":37154,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhQmVLCRA9TVsSAnZWagAAoyEP/20h/hS8d272Dq4Mv637\nGCGk3kVqT86pmvQHprYc7yMaqlHxHnUa9oCAZ38YbRh2I0yUMnpl/McnF9rI\nBpzL6+XCva3kk8zSq50A3ijxl5eNyHDvb2XVC1U2O+1Fj/agZf7siqQue5KR\ncM2lKHyXPwwytYci0TbFoIq9mk7iihpup8CEBbzbBKJvI5T+Xz7bE7aZN4Jy\nXmB3xZrD8xjJ/ZUFftNWLXiWL/zL/kB4oA4AzW1sv9I7wPI/iWyCzWfAn6Zo\nwXo5kkO5QA9yEJ78/N/UljjjdtGIJRNJHUIjPTVkcGZyC/vvL7+FjaadAsok\nd295Qgn2QEEHwuiyH6SKWzuX/UBo/JD9JFqY8MrVMfNlVT1NYW9pTJWNBqYu\nJ0Itq8h7x572w5loxZZ36dNa1HE9CEP/8Lzme3pyZHZb10CLL4GDezJC7K4s\nlbNCLU1oAz9Bb66zYGXx+IPmVd0CLDk9F8TPfstOofj/DKAA6bP9Zp+eBBFk\njEsI8YLwdf0NrOPhu0HwyqrOglRAhBThLO51TXzYQc6/2cWwGLGR1nOUDsPC\n7tszppWFAAz3mRQqpW83MJlR/4oY3EDmZcjKdWH/7Z0SucBk1hIS9B3dGNnM\nlUgPbZEukFcpVLJ4edyBibhSzraumAUjUJNg9waoib1EmztZev0eBBnxICd5\nIul9\r\n=awHn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCaQ7kiPJo7Isd8+3IVr72hFRXSe+y5RLG3Kabs7qvKVQIgY4ZKPhhUgSmY3tqNWUK5LPDU+ItIiKEnraKzo+4zz6Q="}]},"_npmUser":{"name":"garybernhardt","email":"gary.bernhardt@gmail.com"},"directories":{},"maintainers":[{"name":"garybernhardt","email":"gary.bernhardt@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/static-path_0.0.2_1631741259343_0.08745412855903756"},"_hasShrinkwrap":false},"0.0.3":{"name":"static-path","version":"0.0.3","description":"Static-path uses TypeScript to prevent 404s and other path generation mistakes at compile time","main":"lib/index.js","types":"lib/index.d.ts","scripts":{"test":"ava","build":"npm-run-all format compile test lint","compile":"tsc","format":"prettier --write 'src/**/*.ts'","lint":"eslint --max-warnings 0 --ext '.js,.jsx,.ts,.tsx' src","prepare":"npm run build","prepublishOnly":"npm test && npm run lint"},"repository":{"type":"git","url":"git+https://github.com/garybernhardt/static-path.git"},"keywords":["path","url","link","404","pattern","routing","router"],"author":{"name":"Gary Bernhardt"},"license":"ISC","bugs":{"url":"https://github.com/garybernhardt/static-path/issues"},"homepage":"https://github.com/garybernhardt/static-path#readme","devDependencies":{"@typescript-eslint/eslint-plugin":"^4.29.1","@typescript-eslint/parser":"^4.29.1","ava":"^3.15.0","eslint":"^7.32.0","eslint-config-prettier":"^8.3.0","npm-run-all":"^4.1.5","prettier":"^2.3.2","typescript":"^4.3.5"},"gitHead":"d461ed78339e78a41bad0ffa621b62d76c89a305","_id":"static-path@0.0.3","_nodeVersion":"14.17.2","_npmVersion":"6.14.13","dist":{"integrity":"sha512-8o81ywgwIL5OP3ljpW4GAquf2UlHIigHIXTEbksSegouso8/TRRuhCwnBig9iNNGSYtgn7zZQdTi/4NN7Umteg==","shasum":"01a22efdfb0411a907da68923a67d41061dda339","tarball":"https://registry.npmjs.org/static-path/-/static-path-0.0.3.tgz","fileCount":9,"unpackedSize":36866,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH+zOFdHAXB0YI/EzqOAk01rsVZUdgwOYwdEAn/p5adJAiEA2jliGmX1yeLdNA21nuQJA9nMVJTRA3eXmUc9tHLvoHo="}]},"_npmUser":{"name":"garybernhardt","email":"gary.bernhardt@gmail.com"},"directories":{},"maintainers":[{"name":"garybernhardt","email":"gary.bernhardt@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/static-path_0.0.3_1632440280496_0.33762194083148267"},"_hasShrinkwrap":false},"0.0.4":{"name":"static-path","version":"0.0.4","description":"Static-path uses TypeScript to prevent 404s and other path generation mistakes at compile time","main":"lib/index.js","types":"lib/index.d.ts","scripts":{"test":"ava","build":"npm-run-all format compile test lint","compile":"tsc","format":"prettier --write 'src/**/*.ts'","lint":"eslint --max-warnings 0 --ext '.js,.jsx,.ts,.tsx' src","prepare":"npm run build","prepublishOnly":"npm test && npm run lint"},"repository":{"type":"git","url":"git+https://github.com/garybernhardt/static-path.git"},"keywords":["path","url","link","404","pattern","routing","router"],"author":{"name":"Gary Bernhardt"},"license":"ISC","bugs":{"url":"https://github.com/garybernhardt/static-path/issues"},"homepage":"https://github.com/garybernhardt/static-path#readme","devDependencies":{"@typescript-eslint/eslint-plugin":"^4.29.1","@typescript-eslint/parser":"^4.29.1","ava":"^3.15.0","eslint":"^7.32.0","eslint-config-prettier":"^8.3.0","npm-run-all":"^4.1.5","prettier":"^2.3.2","typescript":"^4.3.5"},"gitHead":"1014e4325cb8efde5e1305949fb38c04c258a122","_id":"static-path@0.0.4","_nodeVersion":"14.18.1","_npmVersion":"6.14.15","dist":{"integrity":"sha512-RAe93sPAKMDDuXuyU4UKJ+Sm84XJXk3JH21jlPu3bhs30o9Z3abRF6AE8qZCjCWPlJQrHIy4NfxO5CeYOThLFg==","shasum":"dd30a5117212164c62e2c3e1e61754f9081bf9f1","tarball":"https://registry.npmjs.org/static-path/-/static-path-0.0.4.tgz","fileCount":9,"unpackedSize":42081,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh28CUCRA9TVsSAnZWagAAiPAP/Rx+nLcQgzp+Sp/Np3xR\nGhaYOUfn+sOnA0u1FriC0qfqoWVnmgqtEtR2/tLZpEVfA65o2SD2bTNL/5l0\nuvneVzYrdPbXWhXdfZy/UI8YOsNQZLtk6Em+B4bjNFI7T3bK7sa3LKvXNuKK\n9TRDE84zlZ1yCEuyAsX6x5AxpYjmd18iADZarkR4JiBawEtXoWDecD+Mf7BJ\nlj7VxmWsqFg8bw+m7XwHThOdgs+ovTSnNn1S8R4oP7wmnZZf9R7m3sWOrSZU\nFoNLKaqWNklvPzu4VsmeF/Th8iwtDV4NExKxmxX8Moq+bJ3cq/UNt0HokYgZ\noFJL9GFJj3q77j6gDiQSkNCRz+Cjk3MLHCENGy+b28TdHCl47sTaRpBbh+wz\niv+EGmsYBUNWYdBSuOxMNk0WWv3bAGatXeOImO8/HWiHoC2GgTnPJ2TJur5/\nPYK+F8ISDOcgCuImDRiB2bnLNNG5f3tGfnX0Kwprdzp3kt/a7gICmwiNU8FV\nmdOLczZMoVP3yq6a/BC7rhpI+fSEvfuxtffNL1BcSLWIfptPlEiBAueUbb4m\nW6wZbIpZjRiLMA6e3n5gmMjaY4BcCiil4QcNXSU1i3EcNkrpHJbfjYfQKr8v\nNbhqI5Y+FNKRy3BPVExcS6A0PxP5eB+ZitxvNMWb5LGIEHmaRKoz+Udf7ZWZ\nxBqo\r\n=pmh3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG2A8I3MfZYWLSy7RwQIwu0mAm0lVKUlBWJ9fwjbFdK0AiBDuO61pMAClr6iCRiSg4FPIk8s0tN15eoGuz3iXRvvtg=="}]},"_npmUser":{"name":"garybernhardt","email":"gary.bernhardt@gmail.com"},"directories":{},"maintainers":[{"name":"garybernhardt","email":"gary.bernhardt@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/static-path_0.0.4_1637020279239_0.5903641930790797"},"_hasShrinkwrap":false}},"time":{"created":"2021-08-13T21:57:51.112Z","0.0.1":"2021-08-13T21:57:51.261Z","modified":"2022-05-18T16:26:59.541Z","0.0.2":"2021-09-15T21:27:39.513Z","0.0.3":"2021-09-23T23:38:00.651Z","0.0.4":"2021-11-15T23:51:19.405Z"},"maintainers":[{"name":"garybernhardt","email":"gary.bernhardt@gmail.com"}],"description":"Static-path uses TypeScript to prevent 404s and other path generation mistakes at compile time","homepage":"https://github.com/garybernhardt/static-path#readme","keywords":["path","url","link","404","pattern","routing","router"],"repository":{"type":"git","url":"git+https://github.com/garybernhardt/static-path.git"},"author":{"name":"Gary Bernhardt"},"bugs":{"url":"https://github.com/garybernhardt/static-path/issues"},"license":"ISC","readme":"# static-path\n\nMany web applications have routes defined with Rails/Express-style syntax.\nHere's an example with React Router, but the same idea applies to most routers.\n\n```typescript\n<Route exact path=\"/lessons/:lessonId\" component={Lesson} />\n```\n\nThen other parts of the application link to that route:\n\n```typescript\n<a href={`/lessons/${lessonId}`} />\n```\n\nThe links and the routes don't know about each other.\nEach is just a string.\n\n\nWhat happens when we want to change the structure of the paths?\nFor example, maybe we want to include the lesson's course in the path: `'/courses/:courseId/lessons/:lessonId'`.\nIf we change that route, we also have to change every link that points to a lesson.\nAny links that we miss become 404s.\nAnd it's very easy to miss them!\n\nStatic-path uses the type system to prevent these 404s at compile time.\nIn the scenario above, every link to the lesson page causes a type error because it's missing the new `:courseId` param.\nThat forces us to update each link, ensuring that they match the new route pattern.\n\n## Install\n\n```bash\n# with npm\nnpm install static-path\n\n# with Yarn\nyarn add static-path\n```\n\n## Usage\n\nThink about your paths, router, and links as three separate pieces:\n\n* You define which paths exist.\n* You pass those paths' patterns to your router of choice.\n* You link to those paths.\n\n### Defining paths\n\nCreate a path by calling the `path` function with a Rails/Express-style route pattern.\n\nThe path objects that you get back are functions.\nYou can generate a concrete path by calling them with some params.\nYou'll get a type error if you pass unexpected params, or you omit some params, or you give the params the wrong names.\n\n```typescript\nimport { path } from 'static-path'\n\nconst course = path('/courses/:courseId')\n\n// This returns '/courses/typescript'.\ncourse({courseId: 'typescript'})\n\n// All of these are type errors.\ncourse({courseIdMisnamed: 'typescript'})\ncourse({})\ncourse({courseId: 'typescript', someOtherParam: 'some value'})\n```\n\nWhen you have a path object, you can use it to define subpaths.\nA subpath gets all of its parent's params, plus whatever additional params you define in its route string.\nThis is useful for organizing nested paths without duplicating sections of their patterns.\n\n```typescript\nconst course = path('/courses/:courseId')\nconst lesson = course.path('/lessons/:lessonId')\n\n// This returns '/courses/typescript/lessons/type-predicates'.\nlesson({courseId: 'typescript', lessonId: 'type-predicates'})\n\n// Both of these are type errors.\nlesson({courseId: 'typescript'})\nlesson({lessonId: 'type-predicates'})\n```\n\nStatic-path only supports params like `':courseId'`.\nIts patterns don't support repetition, conditional pattern components, or anything other than literal strings and `:params`.\nIf you need those advanced features, you can always handle them outside of static-path, in whatever way you would've handled them before you read this README!\nOr you can use the pattern shown below in the FAQ.\n\n### Routing\n\nAll static-paths have a `pattern` property with a normalized version of the pattern that you provided.\nYou can pass that pattern to your router of choice.\n\nFor example, [Execute Program](https://www.executeprogram.com) uses [React Router](https://reactrouter.com/) like this (with irrelevant details removed):\n\n```typescript\n// src/client/router.tsx\n\nimport React from 'react'\nimport { BrowserRouter, Route, Switch } from 'react-router-dom'\n\nimport * as paths from '../common/paths'\nimport { Course } from './pages/course'\nimport { Lesson } from './pages/lesson'\n\nexport function Router() {\n  return <BrowserRouter>\n    <Switch>\n      <Route exact path={paths.course.pattern} component={Course} />\n      <Route exact path={paths.lesson.pattern} component={Lesson} />\n    </Switch>\n  </BrowserRouter>\n}\n```\n\nThe paths module is the single source of truth about the patterns, so there's no duplication here.\n(Although it's true that the router module becomes very boring with static-path.\nBut boring code won't hurt you.\nClever code is what will hurt you!)\n\n### Linking\n\nTo link to a path, call it as a function while providing the appropriate params:\n\n```typescript\nimport React from 'react'\nimport * as paths from '../common/paths'\n\nexport function LinkToALesson() {\n  return <a href={paths.lesson({courseId: 'typescript', lessonId: 'type-predicates'})}>\n    Try this lesson!\n  </a>\n}\n```\n\n## Integrating with Express and other server-side routers\n\nRecent versions of [@types/express](https://www.npmjs.com/package/@types/express) can statically determine route parameter names from the route pattern.\nThat feature integrates cleanly with static-path.\n\nThe code below type checks.\nThe `req.params.courseId` property access is allowed because the static-path contains a `:courseId` parameter, and the type information flows from there into the Express `app.get` call.\nIf we try to access `req.params.someOtherParam`, it will type error.\n\n```typescript\nconst course = path('/courses/:courseId');\n\napp.get(course.pattern, (req, res) => {\n  console.log(req.params.courseId;\n  res.end();\n});\n```\n\nOther Express-style routers should work in the same way.\nIf not, those projects need to add param name extraction types similar to those in Express.\n\n## Recommended usage\n\n1. Create a `paths.ts` module to hold your paths.\n   In a large application, you may want to break it up into multiple sub-modules for organization.\n   The important part is that it's a self-contained module or family of modules.\n\n2. Your `paths.ts` should be importable by both your server and your client.\n   That way, code on both sides of the app can safely link to paths.\n   How you achieve this will depend on your web framework.\n   Put `paths.ts` wherever you put other code that's shared by client and server.\n\n3. Always pass a literal string directly to the `path` function, like `path('/courses/:courseId')`.\n   Never pass variables or other expressions to `path`.\n   It's important for `path(...)` to see the literal string type of your pattern.\n   Passing a variable with the type `string`, or a union of multiple strings, may produce a confusing type error.\n   It may also let incorrect links slip through.\n\n4. Generate all of your links via static-paths.\n   Static-path can only help you if you use it to generate your links.\n   Any link created in the old way, like `<a href={`/courses/:courseId`}>`, is invisible to static-path.\n\n## FAQ\n\n### Is this a router?\n\nNo.\nStatic-path doesn't do any routing.\nIt only manages the relationship between patterns like `'/courses/:courseId'`, params like `{courseId: 'typescript'}`, and concrete path strings like `'/courses/typescript'`.\nYou can use it with any router that supports the usual pattern syntax with `:params`.\n\n### What about my bundle size?\n\nStatic-path is [under 1 KiB minified and gzipped](https://bundlephobia.com/package/static-path).\n\n### Can I use this in JavaScript?\n\nYou can technically use static-path in JavaScript, but we don't think there's a reason to.\nThis library's purpose is to statically type code that's particularly prone to dynamic mistakes.\nIn JavaScript, this library can still be used to generate concrete path strings from path objects, but the primary benefit of static type errors is lost.\nWe recommend porting to TypeScript first.\n\n### Is it production-ready?\n\n[Execute Program](https://www.executeprogram.com) has used this library in production since December of 2020.\n\n### How does it work?\n\nIt all hinges on a type that combines [template literal types](https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html) with a [conditional type](https://www.typescriptlang.org/docs/handbook/2/conditional-types.html) to extract the param names from the route pattern string.\nPrior to TypeScript adding template literal type support, this wasn't possible without code generation.\n\n### Doesn't this make the code verbose?\n\nNot really.\nIn fact, if you have your path parameters in appropriately-named local variables then it's shorter than the traditional method.\nHere's a comparison of static-path vs. a hard-coded path in that scenario:\n\n```typescript\n// static-path\n<a href={paths.lesson({courseId, lessonId})} />\n// hard-coded path\n<a href={`/courses/${courseId}/lessons/${lessonId}`} />\n```\n\n### I need fancy route features like conditionals and repetition\n\nThe vast majority of routes don't need these features, but some do.\nIn these cases, you can always write a function.\nStatic-paths are functions that take params, return path strings, and have a `pattern` property.\nYou can define such a function yourself inside of your `paths.ts` module, then use it alongside other static-paths.\n\nHere's an example.\nWe want a path that takes a `courseId` and a `lessonId`.\nBut we also want to allow calls without any `courseId`, in which case we'll use a default course ID instead.\n\n```typescript\n// The function generates concrete path strings, just like static-paths do.\nexport function lessonWithDefault(params: {\n  lessonId: string,\n  courseId?: string\n}) {\n  /* If we don't provide a course ID, assume that we're linking to the\n   * TypeScript course. */\n  const courseId = params.courseId ?? 'typescript'\n\n  return `/courses/${courseId}/lessons/${params.lessonId}`\n}\n\n/* It's up to us to make the `pattern` property match what the function\n * generates. */\nlessonWithDefault.pattern = '/courses/:courseId/lessons/:lessonId'\n```\n\nNote that the `lessonWithDefault` path function doesn't use static-path at all.\nAs a result, static-path can't check for whether the pattern actually matches the params, or whether those match the template literal string that generates the paths.\nThis approach lets you build arbitrarily complex paths, but you have to make sure that your params and patterns line up.\n\nIf you write functions like these, you're still better off than you would be with the traditional method of smashing strings together in an ad-hoc way.\nThis function still serves as a single point of truth describing the paths, rather than writing code to manually build the paths at every point where you link to them.\n\n### What if I hard-code some links instead of calling the path functions?\n\nDon't do it!\nThe benefits of static-path come from using it as the single source of truth about the paths.\nEach time you write ```<a href={`/courses/{courseId}`}>```, you create a link that's invisible to static-path.\nIf you later change the structure of that route, you won't get a type error telling you that that `<a>` tag is out of date.\nAlways use the path objects when linking.\n\n### How do I force a param to be a number or other type?\n\nCall `.toString()` on it.\nStatic-path only supports string values as params.\n\nEncoding type information in the route strings would dramatically increase this library's complexity, if it's possible at all.\nIt would probably lead to worse error messages as well.\nStatic-path is already safer than the traditional approach.\nLet's try to get to 90% before reaching for 100%!\n","readmeFilename":"README.md"}