@feoe/fs-router 提供完整的 TypeScript 支持,确保路由导航的类型安全。
插件会自动分析你的路由结构,生成对应的类型定义:
1// 自动生成的类型文件 src/routes-type.ts
2export interface Routes {
3 '/': {}
4 '/about': {}
5 '/user': {}
6 '/user/:id': { id: string }
7 '/blog/:category/:slug': { category: string; slug: string }
8}
1import { Link } from 'react-router-dom'
2
3// ✅ 类型安全的链接
4<Link to="/user/123">用户详情</Link>
5<Link to="/blog/tech/react-hooks">博客文章</Link>
6
7// ❌ TypeScript 会报错
8<Link to="/invalid-route">无效路由</Link>
1import { useNavigate } from 'react-router-dom'
2
3function MyComponent() {
4 const navigate = useNavigate()
5
6 // ✅ 类型安全的导航
7 const goToUser = (id: string) => {
8 navigate(`/user/${id}`)
9 }
10
11 // ✅ 带参数的导航
12 const goToBlog = (category: string, slug: string) => {
13 navigate(`/blog/${category}/${slug}`)
14 }
15}
1import { useParams } from 'react-router-dom'
2
3// src/routes/user/[id]/page.tsx
4export default function UserPage() {
5 // TypeScript 自动推断 id 为 string 类型
6 const { id } = useParams()
7
8 return <div>用户 ID: {id}</div>
9}
1// src/routes/user/[id]/page.tsx
2interface UserParams {
3 id: string
4}
5
6export default function UserPage() {
7 const { id } = useParams<UserParams>()
8
9 return <div>用户 ID: {id}</div>
10}
1// src/routes/user/[id]/page.tsx
2import { useLoaderData } from 'react-router-dom'
3
4interface User {
5 id: string
6 name: string
7 email: string
8}
9
10export async function loader({ params }): Promise<{ user: User }> {
11 const user = await fetchUser(params.id)
12 return { user }
13}
14
15export default function UserPage() {
16 // TypeScript 自动推断数据类型
17 const { user } = useLoaderData<typeof loader>()
18
19 return (
20 <div>
21 <h1>{user.name}</h1>
22 <p>{user.email}</p>
23 </div>
24 )
25}
1// vite.config.ts
2import { FileBasedRouterVite } from '@feoe/fs-router/vite'
3
4export default defineConfig({
5 plugins: [
6 FileBasedRouterVite({
7 routesDirectory: 'src/routes',
8 generatedRoutesPath: 'src/routes.tsx',
9 // 启用类型生成
10 enableGeneration: true,
11 typeGenerateOptions: {
12 routesTypeFile: 'src/routes-type.ts',
13 // 自定义类型生成选项
14 generateRouteParams: true,
15 generateLoaderTypes: true,
16 routesDirectories: []
17 }
18 })
19 ]
20})
1interface RouteGuard<T = any> {
2 canActivate: (params: T) => boolean | Promise<boolean>
3}
4
5// src/routes/admin/[id]/page.tsx
6export const guard: RouteGuard<{ id: string }> = {
7 canActivate: ({ id }) => {
8 return checkAdminPermission(id)
9 }
10}
1interface RouteMeta {
2 title?: string
3 description?: string
4 requiresAuth?: boolean
5}
6
7// src/routes/user/[id]/page.tsx
8export const meta: RouteMeta = {
9 title: '用户详情',
10 description: '查看用户详细信息',
11 requiresAuth: true
12}
1{
2 "compilerOptions": {
3 "strict": true,
4 "noImplicitAny": true,
5 "strictNullChecks": true,
6 "paths": {
7 "@/*": ["./src/*"],
8 "@/routes": ["./src/routes.tsx"],
9 "@/routes-type": ["./src/routes-type.ts"]
10 }
11 },
12 "include": [
13 "src/**/*",
14 "src/routes-type.ts"
15 ]
16}
1// tsconfig.json
2{
3 "compilerOptions": {
4 "strict": true,
5 "noImplicitReturns": true,
6 "noFallthroughCasesInSwitch": true
7 }
8}
1// 定义清晰的接口
2interface BlogPost {
3 id: string
4 title: string
5 content: string
6 author: User
7 createdAt: Date
8}
9
10export async function loader({ params }) {
11 const post: BlogPost = await fetchBlogPost(params.slug)
12 return { post }
13}
1import { isRouteErrorResponse } from 'react-router-dom'
2
3export default function ErrorBoundary() {
4 const error = useRouteError()
5
6 if (isRouteErrorResponse(error)) {
7 return (
8 <div>
9 <h1>{error.status} {error.statusText}</h1>
10 <p>{error.data}</p>
11 </div>
12 )
13 }
14
15 return <div>未知错误</div>
16}