W precyzyjnych systemach nawigacyjnych Metropolii Quantum 2150, gdzie każdy błędny link może prowadzić do katastrofy, poznasz Typed Routes - eksperymentalną funkcję Next.js, która zapewnia type-safety dla wszystkich ścieżek w aplikacji.
Bez Typed Routes, ścieżki są zwykłymi stringami:
1// ❌ Łatwo o literówkę - brak walidacji w TypeScript
2<Link href="/prodcuts">Produkty</Link> // Literówka!
3<Link href="/users/123/setings">Ustawienia</Link> // Znowu literówka!
4
5// ❌ Brak sprawdzania parametrów
6redirect('/products/' + productId); // Co jeśli productId jest undefined?Typed Routes automatycznie generuje typy dla wszystkich ścieżek w aplikacji.
1// next.config.js
2module.exports = {
3 experimental: {
4 typedRoutes: true,
5 },
6};Po włączeniu i uruchomieniu
next dev lub next build, Next.js wygeneruje plik .next/types/link.d.ts z typami.1import Link from 'next/link';
2
3function Navigation() {
4 return (
5 <nav>
6 {/* ✅ Poprawne ścieżki - TypeScript OK */}
7 <Link href="/">Home</Link>
8 <Link href="/products">Produkty</Link>
9 <Link href="/about">O nas</Link>
10
11 {/* ❌ Błędna ścieżka - TypeScript Error! */}
12 <Link href="/prodcuts">Produkty</Link>
13 {/* Type '"prodcuts"' is not assignable to type 'Route' */}
14 </nav>
15 );
16}1// Dla struktury: app/products/[id]/page.tsx
2<Link href="/products/123">Produkt 123</Link> // ✅ OK
3
4// Dla struktury: app/users/[userId]/posts/[postId]/page.tsx
5<Link href="/users/456/posts/789">Post</Link> // ✅ OK1'use client';
2
3import { useRouter } from 'next/navigation';
4
5function ProductActions({ productId }: { productId: string }) {
6 const router = useRouter();
7
8 const handleEdit = () => {
9 // ✅ TypeScript sprawdza poprawność ścieżki
10 router.push(`/products/${productId}/edit`);
11 };
12
13 const handleDelete = () => {
14 // ✅ Poprawne
15 router.push('/products');
16 };
17
18 const handleBroken = () => {
19 // ❌ TypeScript Error jeśli /prodcts nie istnieje
20 router.push('/prodcts');
21 };
22
23 return (
24 <div>
25 <button onClick={handleEdit}>Edytuj</button>
26 <button onClick={handleDelete}>Usuń</button>
27 </div>
28 );
29}1import { redirect } from 'next/navigation';
2
3async function ProtectedPage() {
4 const session = await getSession();
5
6 if (!session) {
7 // ✅ TypeScript sprawdza ścieżkę
8 redirect('/login');
9 }
10
11 if (!session.isVerified) {
12 // ✅ Poprawne
13 redirect('/verify-email');
14 }
15
16 return <Dashboard />;
17}Next.js generuje typ
Route który możesz używać:1import type { Route } from 'next';
2
3// Funkcja akceptująca tylko poprawne ścieżki
4function navigateTo(path: Route) {
5 window.location.href = path;
6}
7
8navigateTo('/products'); // ✅ OK
9navigateTo('/prodcuts'); // ❌ Error1import type { Route } from 'next';
2
3// Funkcja generująca link do produktu
4function getProductUrl(id: string): Route {
5 return `/products/${id}` as Route;
6}
7
8// Użycie
9<Link href={getProductUrl('123')}>Produkt</Link>1import Link from 'next/link';
2
3function ProductFilters() {
4 return (
5 <div>
6 {/* Ścieżka z query params */}
7 <Link
8 href={{
9 pathname: '/products',
10 query: { category: 'electronics', sort: 'price' },
11 }}
12 >
13 Elektronika (sortuj po cenie)
14 </Link>
15
16 {/* Lub jako string */}
17 <Link href="/products?category=electronics&sort=price">
18 Elektronika
19 </Link>
20 </div>
21 );
22}1// types/navigation.ts
2import type { Route } from 'next';
3
4export interface NavigationItem {
5 label: string;
6 href: Route;
7 icon?: React.ReactNode;
8}
9
10export const mainNavigation: NavigationItem[] = [
11 { label: 'Home', href: '/' },
12 { label: 'Produkty', href: '/products' },
13 { label: 'Kategorie', href: '/categories' },
14 { label: 'O nas', href: '/about' },
15 { label: 'Kontakt', href: '/contact' },
16];
17
18export const userNavigation: NavigationItem[] = [
19 { label: 'Profil', href: '/account/profile' },
20 { label: 'Zamówienia', href: '/account/orders' },
21 { label: 'Ustawienia', href: '/account/settings' },
22];1// components/MainNav.tsx
2import Link from 'next/link';
3import { mainNavigation } from '@/types/navigation';
4
5export function MainNav() {
6 return (
7 <nav className="flex gap-6">
8 {mainNavigation.map((item) => (
9 <Link
10 key={item.href}
11 href={item.href} // TypeScript wie, że to poprawna ścieżka
12 className="hover:text-blue-500"
13 >
14 {item.label}
15 </Link>
16 ))}
17 </nav>
18 );
19}1// components/Breadcrumbs.tsx
2import Link from 'next/link';
3import type { Route } from 'next';
4
5interface BreadcrumbItem {
6 label: string;
7 href?: Route;
8}
9
10interface BreadcrumbsProps {
11 items: BreadcrumbItem[];
12}
13
14export function Breadcrumbs({ items }: BreadcrumbsProps) {
15 return (
16 <nav aria-label="Breadcrumb" className="flex items-center gap-2 text-sm">
17 {items.map((item, index) => (
18 <span key={index} className="flex items-center gap-2">
19 {index > 0 && <span className="text-gray-400">/</span>}
20 {item.href ? (
21 <Link href={item.href} className="text-blue-600 hover:underline">
22 {item.label}
23 </Link>
24 ) : (
25 <span className="text-gray-600">{item.label}</span>
26 )}
27 </span>
28 ))}
29 </nav>
30 );
31}
32
33// Użycie
34<Breadcrumbs
35 items={[
36 { label: 'Home', href: '/' },
37 { label: 'Produkty', href: '/products' },
38 { label: 'Elektronika', href: '/products?category=electronics' },
39 { label: 'Smartphone X' }, // Ostatni element bez linku
40 ]}
41/>1// ❌ TypeScript nie może sprawdzić wartości dynamicznego segmentu
2const id = getUserInput();
3<Link href={`/products/${id}`}>Produkt</Link> // Przyjmie każdy string
4
5// ✅ Możesz użyć asercji typu jeśli jesteś pewien
6<Link href={`/products/${id}` as Route}>Produkt</Link>1// ❌ Zewnętrzne URL nie są Route
2<Link href="https://google.com">Google</Link> // Error
3
4// ✅ Użyj zwykłego <a> dla zewnętrznych linków
5<a href="https://google.com">Google</a>1// Dla app/docs/[...slug]/page.tsx
2// TypeScript sprawdzi prefix, ale nie całą ścieżkę
3<Link href="/docs/getting-started/installation">Docs</Link> // ✅1// next.config.js
2module.exports = {
3 experimental: {
4 typedRoutes: true,
5 },
6};1npm run dev
2# lub
3npm run build1# Sprawdź błędy
2npm run type-check
3
4# Typowe problemy:
5# - Literówki w ścieżkach
6# - Nieistniejące strony
7# - Zewnętrzne linki w <Link>Jeśli typy nie działają poprawnie:
1# Usuń wygenerowane typy i wygeneruj ponownie
2rm -rf .next/types
3npm run devSprawdź wygenerowany plik:
1// .next/types/link.d.ts
2// Ten plik zawiera wszystkie wygenerowane typy RouteTyped Routes w Next.js zapewniają:
W precyzyjnych systemach Metropolii Quantum 2150, gdzie każdy błąd nawigacyjny może kosztować miliony, Typed Routes to niezbędne zabezpieczenie dla każdej profesjonalnej aplikacji Next.js!