frontend-plugin-api: cleanup common and useRouteRef type declarations

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2023-10-12 12:05:41 +02:00
parent 3ad2429209
commit ae212c9169
3 changed files with 46 additions and 74 deletions
@@ -14,8 +14,6 @@
* limitations under the License.
*/
import { getOrCreateGlobalSingleton } from '@backstage/version-bridge';
/**
* Catch-all type for route params.
*
@@ -23,53 +21,6 @@ import { getOrCreateGlobalSingleton } from '@backstage/version-bridge';
*/
export type AnyRouteParams = { [param in string]: string } | undefined;
/**
* TS magic for handling route parameters.
*
* @remarks
*
* The extra TS magic here is to require a single params argument if the RouteRef
* had at least one param defined, but require 0 arguments if there are no params defined.
* Without this we'd have to pass in empty object to all parameter-less RouteRefs
* just to make TypeScript happy, or we would have to make the argument optional in
* which case you might forget to pass it in when it is actually required.
*
* @public
*/
export type RouteFunc<Params extends AnyParams> = (
...[params]: Params extends undefined ? readonly [] : readonly [Params]
) => string;
/**
* This symbol is what we use at runtime to determine whether a given object
* is a type of RouteRef or not. It doesn't work well in TypeScript though since
* the `unique symbol` will refer to different values between package versions.
* For that reason we use the marker $$routeRefType to represent the symbol at
* compile-time instead of using the symbol directly.
*
* @internal
*/
export const routeRefType: unique symbol = getOrCreateGlobalSingleton<any>(
'route-ref-type',
() => Symbol('route-ref-type'),
);
/**
* Optional route params.
*
* @internal
*/
export type OptionalParams<Params extends { [param in string]: string }> =
Params[keyof Params] extends never ? undefined : Params;
/**
* Type describing the key type of a route parameter mapping.
*
* @ignore
*/
export type ParamKeys<TParams extends AnyRouteParams> =
keyof TParams extends never ? [] : (keyof TParams)[];
/**
* A duplicate of the react-router RouteObject, but with routeRef added
* @internal
@@ -17,25 +17,41 @@
import { useMemo } from 'react';
import { matchRoutes, useLocation } from 'react-router-dom';
import { useVersionedContext } from '@backstage/version-bridge';
import {
AnyParams,
ExternalRouteRef,
RouteFunc,
RouteRef,
SubRouteRef,
} from './types';
import { AnyRouteParams } from './types';
import { RouteRef } from './RouteRef';
import { SubRouteRef } from './SubRouteRef';
import { ExternalRouteRef } from './ExternalRouteRef';
/**
* TS magic for handling route parameters.
*
* @remarks
*
* The extra TS magic here is to require a single params argument if the RouteRef
* had at least one param defined, but require 0 arguments if there are no params defined.
* Without this we'd have to pass in empty object to all parameter-less RouteRefs
* just to make TypeScript happy, or we would have to make the argument optional in
* which case you might forget to pass it in when it is actually required.
*
* @public
*/
export type RouteFunc<TParams extends AnyRouteParams> = (
...[params]: TParams extends undefined
? readonly []
: readonly [params: TParams]
) => string;
/**
* @internal
*/
export interface RouteResolver {
resolve<Params extends AnyParams>(
resolve<TParams extends AnyRouteParams>(
anyRouteRef:
| RouteRef<Params>
| SubRouteRef<Params>
| ExternalRouteRef<Params, any>,
| RouteRef<TParams>
| SubRouteRef<TParams>
| ExternalRouteRef<TParams, any>,
sourceLocation: Parameters<typeof matchRoutes>[1],
): RouteFunc<Params> | undefined;
): RouteFunc<TParams> | undefined;
}
/**
@@ -49,9 +65,12 @@ export interface RouteResolver {
* @returns A function that will in turn return the concrete URL of the `routeRef`.
* @public
*/
export function useRouteRef<Optional extends boolean, Params extends AnyParams>(
routeRef: ExternalRouteRef<Params, Optional>,
): Optional extends true ? RouteFunc<Params> | undefined : RouteFunc<Params>;
export function useRouteRef<
TOptional extends boolean,
TParams extends AnyRouteParams,
>(
routeRef: ExternalRouteRef<TParams, TOptional>,
): TParams extends true ? RouteFunc<TParams> | undefined : RouteFunc<TParams>;
/**
* React hook for constructing URLs to routes.
@@ -64,9 +83,9 @@ export function useRouteRef<Optional extends boolean, Params extends AnyParams>(
* @returns A function that will in turn return the concrete URL of the `routeRef`.
* @public
*/
export function useRouteRef<Params extends AnyParams>(
routeRef: RouteRef<Params> | SubRouteRef<Params>,
): RouteFunc<Params>;
export function useRouteRef<TParams extends AnyRouteParams>(
routeRef: RouteRef<TParams> | SubRouteRef<TParams>,
): RouteFunc<TParams>;
/**
* React hook for constructing URLs to routes.
@@ -79,12 +98,12 @@ export function useRouteRef<Params extends AnyParams>(
* @returns A function that will in turn return the concrete URL of the `routeRef`.
* @public
*/
export function useRouteRef<Params extends AnyParams>(
export function useRouteRef<TParams extends AnyRouteParams>(
routeRef:
| RouteRef<Params>
| SubRouteRef<Params>
| ExternalRouteRef<Params, any>,
): RouteFunc<Params> | undefined {
| RouteRef<TParams>
| SubRouteRef<TParams>
| ExternalRouteRef<TParams, any>,
): RouteFunc<TParams> | undefined {
const { pathname } = useLocation();
const versionedContext = useVersionedContext<{ 1: RouteResolver }>(
'routing-context',
@@ -15,14 +15,16 @@
*/
import { useParams } from 'react-router-dom';
import { RouteRef, AnyParams, SubRouteRef } from './types';
import { AnyRouteParams } from './types';
import { RouteRef } from './RouteRef';
import { SubRouteRef } from './SubRouteRef';
/**
* React hook for retrieving dynamic params from the current URL.
* @param _routeRef - Ref of the current route.
* @public
*/
export function useRouteRefParams<Params extends AnyParams>(
export function useRouteRefParams<Params extends AnyRouteParams>(
_routeRef: RouteRef<Params> | SubRouteRef<Params>,
): Params {
return useParams() as Params;