Die meisten von TanStack Router bereitgestellten Typen sind intern, unterliegen Breaking Changes und sind nicht immer einfach zu verwenden. Aus diesem Grund verfügt TanStack Router über eine Teilmenge von bereitgestellten Typen, die auf Benutzerfreundlichkeit ausgelegt sind und extern verwendet werden können. Diese Typen bieten dieselbe typsichere Erfahrung aus den Laufzeitkonzepten von TanStack Router auf der Typpentypenebene, mit der Flexibilität, wo die Typüberprüfung bereitgestellt werden kann.
ValidateLinkOptions überprüft Literal-Objekttypen, um sicherzustellen, dass sie an den Inferenzstellen den Link-Optionen entsprechen. Sie können beispielsweise eine generische HeadingLink-Komponente haben, die eine title-Prop zusammen mit linkOptions akzeptiert. Die Idee dahinter ist, dass diese Komponente für jede Navigation wiederverwendet werden kann.
export interface HeaderLinkProps<
TRouter extends RegisteredRouter = RegisteredRouter,
TOptions = unknown,
> {
title: string
linkOptions: ValidateLinkOptions<TRouter, TOptions>
}
export function HeadingLink<TRouter extends RegisteredRouter, TOptions>(
props: HeaderLinkProps<TRouter, TOptions>,
): React.ReactNode
export function HeadingLink(props: HeaderLinkProps): React.ReactNode {
return (
<>
<h1>{props.title}</h1>
<Link {...props.linkOptions} />
</>
)
}
export interface HeaderLinkProps<
TRouter extends RegisteredRouter = RegisteredRouter,
TOptions = unknown,
> {
title: string
linkOptions: ValidateLinkOptions<TRouter, TOptions>
}
export function HeadingLink<TRouter extends RegisteredRouter, TOptions>(
props: HeaderLinkProps<TRouter, TOptions>,
): React.ReactNode
export function HeadingLink(props: HeaderLinkProps): React.ReactNode {
return (
<>
<h1>{props.title}</h1>
<Link {...props.linkOptions} />
</>
)
}
Eine permissivere Überladung von HeadingLink wird verwendet, um Typassertionen zu vermeiden, die Sie sonst mit der generischen Signatur durchführen müssten. Die Verwendung einer lockeren Signatur ohne Typparameter ist eine einfache Möglichkeit, Typassertionen in der Implementierung von HeadingLink zu vermeiden.
Alle Typparameter für Utilities sind optional, aber für die beste TypeScript-Performance sollte TRouter immer für die öffentlich zugängliche Signatur angegeben werden. Und TOptions sollte immer an Inferenzstellen wie HeadingLink verwendet werden, um die linkOptions zu inferieren und params und search korrekt einzugrenzen.
Das Ergebnis ist, dass linkOptions in der folgenden Darstellung vollständig typsicher sind.
<HeadingLink title="Posts" linkOptions={{ to: '/posts' }} />
<HeadingLink title="Post" linkOptions={{ to: '/posts/$postId', params: {postId: 'postId'} }} />
<HeadingLink title="Posts" linkOptions={{ to: '/posts' }} />
<HeadingLink title="Post" linkOptions={{ to: '/posts/$postId', params: {postId: 'postId'} }} />
Alle Navigations-Typ-Utilities haben eine Array-Variante. ValidateLinkOptionsArray ermöglicht die Typüberprüfung eines Arrays von Link-Optionen. Sie könnten beispielsweise eine generische Menu-Komponente haben, bei der jedes Element ein Link ist.
export interface MenuProps<
TRouter extends RegisteredRouter = RegisteredRouter,
TItems extends ReadonlyArray<unknown> = ReadonlyArray<unknown>,
> {
items: ValidateLinkOptionsArray<TRouter, TItems>
}
export function Menu<
TRouter extends RegisteredRouter = RegisteredRouter,
TItems extends ReadonlyArray<unknown>,
>(props: MenuProps<TRouter, TItems>): React.ReactNode
export function Menu(props: MenuProps): React.ReactNode {
return (
<ul>
{props.items.map((item) => (
<li>
<Link {...item} />
</li>
))}
</ul>
)
}
export interface MenuProps<
TRouter extends RegisteredRouter = RegisteredRouter,
TItems extends ReadonlyArray<unknown> = ReadonlyArray<unknown>,
> {
items: ValidateLinkOptionsArray<TRouter, TItems>
}
export function Menu<
TRouter extends RegisteredRouter = RegisteredRouter,
TItems extends ReadonlyArray<unknown>,
>(props: MenuProps<TRouter, TItems>): React.ReactNode
export function Menu(props: MenuProps): React.ReactNode {
return (
<ul>
{props.items.map((item) => (
<li>
<Link {...item} />
</li>
))}
</ul>
)
}
Dies ermöglicht es natürlich, dass die folgende items-Prop vollständig typsicher ist.
<Menu
items={[
{ to: '/posts' },
{ to: '/posts/$postId', params: { postId: 'postId' } },
]}
/>
<Menu
items={[
{ to: '/posts' },
{ to: '/posts/$postId', params: { postId: 'postId' } },
]}
/>
Es ist auch möglich, from für jede Link-Option im Array festzulegen. Dies würde es allen Menu-Elementen ermöglichen, relativ zu from zu navigieren. Zusätzliche Typüberprüfung von from kann durch das Utility ValidateFromPath bereitgestellt werden.
export interface MenuProps<
TRouter extends RegisteredRouter = RegisteredRouter,
TItems extends ReadonlyArray<unknown> = ReadonlyArray<unknown>,
TFrom extends string = string,
> {
from: ValidateFromPath<TRouter, TFrom>
items: ValidateLinkOptionsArray<TRouter, TItems, TFrom>
}
export function Menu<
TRouter extends RegisteredRouter = RegisteredRouter,
TItems extends ReadonlyArray<unknown>,
TFrom extends string = string,
>(props: MenuProps<TRouter, TItems, TFrom>): React.ReactNode
export function Menu(props: MenuProps): React.ReactNode {
return (
<ul>
{props.items.map((item) => (
<li>
<Link {...item} from={props.from} />
</li>
))}
</ul>
)
}
export interface MenuProps<
TRouter extends RegisteredRouter = RegisteredRouter,
TItems extends ReadonlyArray<unknown> = ReadonlyArray<unknown>,
TFrom extends string = string,
> {
from: ValidateFromPath<TRouter, TFrom>
items: ValidateLinkOptionsArray<TRouter, TItems, TFrom>
}
export function Menu<
TRouter extends RegisteredRouter = RegisteredRouter,
TItems extends ReadonlyArray<unknown>,
TFrom extends string = string,
>(props: MenuProps<TRouter, TItems, TFrom>): React.ReactNode
export function Menu(props: MenuProps): React.ReactNode {
return (
<ul>
{props.items.map((item) => (
<li>
<Link {...item} from={props.from} />
</li>
))}
</ul>
)
}
ValidateLinkOptionsArray ermöglicht es Ihnen, from durch Angabe eines zusätzlichen Typparameters festzulegen. Das Ergebnis ist ein typsicheres Array von Link-Optionen, die Navigation relativ zu from bereitstellen.
<Menu
from="/posts"
items={[{ to: '.' }, { to: './$postId', params: { postId: 'postId' } }]}
/>
<Menu
from="/posts"
items={[{ to: '.' }, { to: './$postId', params: { postId: 'postId' } }]}
/>
ValidateRedirectOptions überprüft Literal-Objekttypen, um sicherzustellen, dass sie an den Inferenzstellen den Redirect-Optionen entsprechen. Sie benötigen möglicherweise beispielsweise eine generische Funktion fetchOrRedirect, die eine url zusammen mit redirectOptions akzeptiert. Die Idee ist, dass diese Funktion umleitet, wenn der fetch fehlschlägt.
export async function fetchOrRedirect<
TRouter extends RegisteredRouter = RegisteredRouter,
TOptions,
>(
url: string,
redirectOptions: ValidateRedirectOptions<TRouter, TOptions>,
): Promise<unknown>
export async function fetchOrRedirect(
url: string,
redirectOptions: ValidateRedirectOptions,
): Promise<unknown> {
const response = await fetch(url)
if (!response.ok && response.status === 401) {
throw redirect(redirectOptions)
}
return await response.json()
}
export async function fetchOrRedirect<
TRouter extends RegisteredRouter = RegisteredRouter,
TOptions,
>(
url: string,
redirectOptions: ValidateRedirectOptions<TRouter, TOptions>,
): Promise<unknown>
export async function fetchOrRedirect(
url: string,
redirectOptions: ValidateRedirectOptions,
): Promise<unknown> {
const response = await fetch(url)
if (!response.ok && response.status === 401) {
throw redirect(redirectOptions)
}
return await response.json()
}
Das Ergebnis ist, dass redirectOptions, die an fetchOrRedirect übergeben werden, vollständig typsicher sind.
fetchOrRedirect('http://example.com/', { to: '/login' })
fetchOrRedirect('http://example.com/', { to: '/login' })
ValidateNavigateOptions überprüft Literal-Objekttypen, um sicherzustellen, dass sie an den Inferenzstellen den Navigate-Optionen entsprechen. Sie möchten beispielsweise einen benutzerdefinierten Hook schreiben, um die Navigation zu aktivieren/deaktivieren.
export interface UseConditionalNavigateResult {
enable: () => void
disable: () => void
navigate: () => void
}
export function useConditionalNavigate<
TRouter extends RegisteredRouter = RegisteredRouter,
TOptions,
>(
navigateOptions: ValidateNavigateOptions<TRouter, TOptions>,
): UseConditionalNavigateResult
export function useConditionalNavigate(
navigateOptions: ValidateNavigateOptions,
): UseConditionalNavigateResult {
const [enabled, setEnabled] = useState(false)
const navigate = useNavigate()
return {
enable: () => setEnabled(true),
disable: () => setEnabled(false),
navigate: () => {
if (enabled) {
navigate(navigateOptions)
}
},
}
}
export interface UseConditionalNavigateResult {
enable: () => void
disable: () => void
navigate: () => void
}
export function useConditionalNavigate<
TRouter extends RegisteredRouter = RegisteredRouter,
TOptions,
>(
navigateOptions: ValidateNavigateOptions<TRouter, TOptions>,
): UseConditionalNavigateResult
export function useConditionalNavigate(
navigateOptions: ValidateNavigateOptions,
): UseConditionalNavigateResult {
const [enabled, setEnabled] = useState(false)
const navigate = useNavigate()
return {
enable: () => setEnabled(true),
disable: () => setEnabled(false),
navigate: () => {
if (enabled) {
navigate(navigateOptions)
}
},
}
}
Das Ergebnis ist, dass navigateOptions, die an useConditionalNavigate übergeben werden, vollständig typsicher sind und wir die Navigation basierend auf dem React-Status aktivieren/deaktivieren können.
const { enable, disable, navigate } = useConditionalNavigate({
to: '/posts/$postId',
params: { postId: 'postId' },
})
const { enable, disable, navigate } = useConditionalNavigate({
to: '/posts/$postId',
params: { postId: 'postId' },
})
Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.