如何在Next.js中声明类型安全的任意有效Route数组?
如何在NextJS静态类型化路由中声明类型安全的任意有效Route数组?
TL;DR: 借助NextJS的静态类型化路由,如何声明一个类型安全的任意有效Route数组?
我有一个启用了typedRoutes的NextJS v15(app router)项目,文件结构如下:
src/app/page.tsx src/app/projects/page.tsx src/app/projects/[projectId]/page.tsx
这意味着我可以编写如下代码,Next会自动校验路由的有效性:
import Link from 'next/link'; function SomeValidLinks(){ const projectId = '123'; return [ <Link href='/'>Root</Link>, <Link href='/projects'>Projects</Link>, <Link href={`/projects/${encodeURIComponent(projectId)}`}>Project {projectId}</Link>, ]; } function DoesNotWork(){ // 类型校验错误:Type "/banana" is not assignable to type 'UrlObject | RouteImpl<"/banana">'. return <Link href='/banana'>Invalid route</Link>; }
这一特性很实用,但我无法找到声明任意有效Route值数组的方法。例如以下代码:
const routes: Route<string>[] = []; routes.push('/'); // ✅ routes.push('/projects'); // ✅ routes.push(`/projects/${encodeURIComponent(projectId)}`); // 💥
动态路由无法赋值给Route<string>,最后一行会报错:Argument of type '/projects/${string}' is not assignable to parameter of type 'Route<string>'.
因此我的核心问题是:如何声明一个包含有效Route的数组?
我尝试过以下无效方案:
- 将数组声明为
Route[],这与Route<string>等价,无法解决问题 - 将数组声明为
Route<any>[],这会破坏类型安全性,等同于any[],可存入错误链接 - 将数组声明为
Route<unknown>[],会报错:Type 'unknown' does not satisfy the constraint 'string'. - 尝试类似Java中
Route<?>的写法,表示只要是Route<X>即可,不关心X的具体类型,但TypeScript不支持该语法
目前唯一勉强可行的方案是使用泛型和类型推断:
function doSomethingWithRoutes<T extends string>(routes: Route<T>[]) { // 处理路由数组 } doSomethingWithRoutes('/', '/projects', `/projects/${encodeURIComponent(projectId)}`); // ✅ doSomethingWithRoutes('/banana'); // 💥
但这种方式十分冗长繁琐,所有接收路由数组的函数都需要泛型参数化,且无法显式为变量指定类型,因此我并不喜欢这种方案。
更具体的示例:
我的实际场景之一是菜单组件,希望构建一个可接收菜单项数组并渲染菜单的组件,示例如下:
const menu = <NavMenu items={[ { url: '/', label: 'Overview' }, { url: '/projects', label: 'Projects' }, { url: '/applications', label: 'Applications' }, { url: '/me', label: 'Your profile' }, ]} />;
为了在url字段中使用类型安全的路由,我必须将组件及其props设置为泛型,这会导致代码重复、可读性下降,并且影响所有使用该组件的代码:
type MenuItem<T extends string> = { url: Route<T>; label: string; }; function NavMenu<T extends string>(props: { items: MenuItem<T>[] }) { // 渲染菜单 }
如果我在某个地方添加Route属性、参数或字段,所有相关代码都需要改为泛型,这意味着一个小改动可能会影响整个代码库的大量部分。
内容的提问来源于stack exchange,提问作者gustafc
相关产品推荐
相关产品推荐

