TypeScript中如何实现类型安全的路由系统
作者:代码保安
本文讲述TypeScript如何实现类型安全的路由系统,路由类型结构、使用infer和Extract提取参数类型、定义路由配置类型、创建路由表、实现类型安全的导航和位置钩子等,使用React Router v6或Next.js App Router,结合zod进行查询参数验证,以实现类型安全的路由系统
在 TypeScript 中实现一个类型安全的路由系统,可以通过 类型推导 和 类型约束 来确保路由路径、参数、查询参数和组件类型之间的强一致性。以下是实现一个类型安全路由系统的完整方案:
目标
- 路由路径类型安全(路径错误编译时报错)
- 路由参数类型安全(路径参数类型正确)
- 查询参数类型安全(查询参数类型正确)
- 路由组件类型安全(路由组件类型正确)
- 支持嵌套路由
1. 定义路由类型结构
我们使用一个 RouteConfig 类型来定义路由结构,支持嵌套和参数。
type RouteConfig<T extends string> = {
path: T;
component: React.ComponentType<any>;
children?: RouteConfig<any>[];
params?: Record<string, string>; // 可选参数类型
query?: Record<string, string>; // 查询参数类型
};
但更推荐使用 泛型 + 映射类型 来实现类型安全。
2. 使用infer和Extract实现路径参数类型推导
我们使用 infer 来从路径字符串中提取参数类型。
// 从路径字符串中提取参数类型
type PathParams<T extends string> = T extends `${infer P}/${infer R}`
? P extends `:${infer Param}`
? { [K in Param]: string } & PathParams<R>
: PathParams<R>
: {};
示例:
type P1 = PathParams<'users/:id'>; // { id: string }
type P2 = PathParams<'users/:id/posts/:postId'>; // { id: string; postId: string }
3. 定义路由配置类型(支持嵌套)
type Route<T extends string> = {
path: T;
component: React.ComponentType<any>;
children?: Route<T>[];
params?: PathParams<T>; // 参数类型
query?: Record<string, string>; // 查询参数类型
};
4. 创建路由表(类型安全)
const routes = {
home: {
path: '/',
component: Home,
},
users: {
path: '/users',
component: Users,
children: [
{
path: '/users/:id',
component: UserDetail,
params: { id: 'string' },
},
{
path: '/users/:id/posts/:postId',
component: PostDetail,
params: { id: 'string', postId: 'string' },
},
],
},
} as const;
使用
as const强制类型推导为字面量类型,防止类型丢失。
5. 实现类型安全的useNavigate和useLocation
useNavigate类型安全
type NavigateOptions<T extends string> = {
to: T;
query?: Record<string, string>;
};
function useNavigate() {
const navigate = useNavigate(); // 假设使用 React Router v6
return <T extends string>(options: NavigateOptions<T>) => {
const { to, query } = options;
const search = query ? new URLSearchParams(query).toString() : '';
navigate(`${to}${search ? `?${search}` : ''}`);
};
}
useLocation类型安全
function useLocation<T extends string>() {
const location = useLocation();
const path = location.pathname as T;
// 提取路径参数
const params = path.split('/').reduce((acc, segment, index, arr) => {
if (segment.startsWith(':')) {
const key = segment.slice(1);
const value = arr[index + 1];
return { ...acc, [key]: value };
}
return acc;
}, {} as Record<string, string>);
// 查询参数
const searchParams = new URLSearchParams(location.search);
const query = {} as Record<string, string>;
searchParams.forEach((value, key) => {
query[key] = value;
});
return { path, params, query };
}
6. 使用useNavigate和useLocation的示例
function UserDetail() {
const { params, query } = useLocation<'/users/:id'>();
const navigate = useNavigate();
return (
<div>
User: {params.id}
<button onClick={() => navigate({ to: '/users', query: { page: '2' } })}>
Go to Users
</button>
</div>
);
}
7. 类型安全的Link组件
type LinkProps<T extends string> = {
to: T;
query?: Record<string, string>;
children: React.ReactNode;
};
function Link<T extends string>({ to, query, children }: LinkProps<T>) {
const navigate = useNavigate();
return (
<button onClick={() => navigate({ to, query })}>
{children}
</button>
);
}
8. 使用React Router的类型安全封装
如果你使用 react-router-dom,可以使用 useParams 和 useSearchParams 并结合类型推导。
function UserDetail() {
const { id } = useParams<{ id: string }>();
const [searchParams] = useSearchParams();
const postId = searchParams.get('postId');
return <div>User {id} Post {postId}</div>;
}
useParams和useSearchParams本身支持类型推导,但需要手动指定类型。
9. 高级:使用zod+TypeScript实现查询参数验证
import { z } from 'zod';
const UserQuerySchema = z.object({
page: z.string().optional(),
limit: z.string().optional(),
});
type UserQuery = z.infer<typeof UserQuerySchema>;
然后在 useLocation 中使用:
const query = UserQuerySchema.parse(Object.fromEntries(searchParams));
10. 总结:类型安全路由系统的关键点
| 功能 | 实现方式 |
|---|---|
| 路径类型安全 | 使用 infer 提取路径参数 |
| 参数类型安全 | params 字段显式定义类型 |
| 查询参数类型安全 | query 字段定义类型,配合 zod 验证 |
| 组件类型安全 | component 类型为 React.ComponentType<any> |
| 路由跳转安全 | useNavigate 类型约束 to 路径 |
| 嵌套路由 | 使用 children 字段递归定义 |
最终建议
- 使用
React Router v6或Next.js App Router(推荐) - 使用
zod验证查询参数 - 使用
as const固定路由配置类型 - 使用
infer提取路径参数类型 - 使用
useNavigate和useLocation封装类型安全接口
示例项目结构
src/
routes/
index.ts
user.ts
components/
Home.tsx
UserDetail.tsx
hooks/
useNavigate.ts
useLocation.ts
通过以上方式,你可以构建一个 完全类型安全 的路由系统,避免运行时错误,提升开发体验。
到此这篇关于TypeScript中如何实现类型安全的路由系统的文章就介绍到这了,更多相关TypeScript实现路由系统内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
