Skip to content

Commit

Permalink
Less strict children type in Switch.
Browse files Browse the repository at this point in the history
  • Loading branch information
molefrog committed Nov 3, 2022
1 parent 5d21dde commit 8f943ab
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 76 deletions.
17 changes: 6 additions & 11 deletions types/ts3.9.4/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,9 @@ export type LinkProps<H extends BaseLocationHook = LocationHook> = Omit<
> &
NavigationalProps<H>;

export type RedirectProps<H extends BaseLocationHook = LocationHook> =
NavigationalProps<H> & {
children?: never;
};
export type RedirectProps<H extends BaseLocationHook = LocationHook> = NavigationalProps<H> & {
children?: never;
};

export function Redirect<H extends BaseLocationHook = LocationHook>(
props: PropsWithChildren<RedirectProps<H>>,
Expand All @@ -78,7 +77,7 @@ export function Link<H extends BaseLocationHook = LocationHook>(

export interface SwitchProps {
location?: string;
children: Array<ReactElement<RouteProps>>;
children: ReactNode;
}
export const Switch: FunctionComponent<SwitchProps>;

Expand All @@ -103,12 +102,8 @@ export const Router: FunctionComponent<

export function useRouter(): RouterProps;

export function useRoute<T extends DefaultParams = DefaultParams>(
pattern: Path
): Match<T>;
export function useRoute<T extends DefaultParams = DefaultParams>(pattern: Path): Match<T>;

export function useLocation<
H extends BaseLocationHook = LocationHook
>(): HookReturnValue<H>;
export function useLocation<H extends BaseLocationHook = LocationHook>(): HookReturnValue<H>;

// tslint:enable:no-unnecessary-generics
28 changes: 18 additions & 10 deletions types/ts3.9.4/type-specs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,11 @@ const invalidParamsWithGeneric: Params<{ id: number }> = { id: 13 }; // $ExpectE
This is a <b>mixed</b> content
</Route>;

<Route path="/users/:id">
{(params: Params): React.ReactNode => `User id: ${params.id}`}
</Route>;
<Route path="/users/:id">{(params: Params): React.ReactNode => `User id: ${params.id}`}</Route>;

<Route<{ id: string }> path="/users/:id">{({ id }) => `User id: ${id}`}</Route>;

<Route<{ id: string }> path="/users/:id">
{({ age }) => `User age: ${age}`} // $ExpectError
</Route>;
<Route<{ id: string }> path="/users/:id">{({ age }) => `User age: ${age}`}</Route>; // $ExpectError

<Route path="/app" match={true} />; // $ExpectError

Expand Down Expand Up @@ -128,10 +124,7 @@ const invalidParamsWithGeneric: Params<{ id: number }> = { id: 13 }; // $ExpectE

Redirect<UseNetworkLocation>({ href: "/home", delay: 1000 });
// example custom hook
type UseLocWithNoOptions = () => [
string,
(to: string, foo: number, bar: string) => void
];
type UseLocWithNoOptions = () => [string, (to: string, foo: number, bar: string) => void];
Redirect<UseLocWithNoOptions>({ href: "/app" });

<Redirect>something</Redirect>; // $ExpectError
Expand All @@ -150,6 +143,21 @@ Redirect<UseLocWithNoOptions>({ href: "/app" });
<Route />
</Switch>;

<Switch>
This won't be rendered, but it's allowed
<Route path="/app/users" />
<>
<div />
I'm a fragment
</>
{false && <a>Conditionals</a>}
{null}
{undefined}
<Route />
</Switch>;

<Switch />; // $ExpectError

/*
* Router specs
*/
Expand Down
68 changes: 29 additions & 39 deletions types/ts4.1/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Minimum TypeScript Version: 4.1

// tslint:disable:no-unnecessary-generics

import {
Expand All @@ -19,33 +20,31 @@ import {
} from "../use-location";

import { DefaultParams, Params, Match, MatcherFn } from "../matcher";
import React = require("react");

// re-export types from these modules
export * from "../matcher";
export * from "../use-location";

export type ExtractRouteOptionalParam<PathType extends Path> =
PathType extends `${infer Param}?`
? { [k in Param]: string | undefined }
: PathType extends `${infer Param}*`
? { [k in Param]: string | undefined }
: PathType extends `${infer Param}+`
? { [k in Param]: string }
: { [k in PathType]: string };

export type ExtractRouteParams<PathType extends string> =
string extends PathType
? { [k in string]: string }
: PathType extends `${infer _Start}:${infer ParamWithOptionalRegExp}/${infer Rest}`
? ParamWithOptionalRegExp extends `${infer Param}(${infer _RegExp})`
? ExtractRouteOptionalParam<Param> & ExtractRouteParams<Rest>
: ExtractRouteOptionalParam<ParamWithOptionalRegExp> &
ExtractRouteParams<Rest>
: PathType extends `${infer _Start}:${infer ParamWithOptionalRegExp}`
? ParamWithOptionalRegExp extends `${infer Param}(${infer _RegExp})`
? ExtractRouteOptionalParam<Param>
: ExtractRouteOptionalParam<ParamWithOptionalRegExp>
: {};
export type ExtractRouteOptionalParam<PathType extends Path> = PathType extends `${infer Param}?`
? { [k in Param]: string | undefined }
: PathType extends `${infer Param}*`
? { [k in Param]: string | undefined }
: PathType extends `${infer Param}+`
? { [k in Param]: string }
: { [k in PathType]: string };

export type ExtractRouteParams<PathType extends string> = string extends PathType
? { [k in string]: string }
: PathType extends `${infer _Start}:${infer ParamWithOptionalRegExp}/${infer Rest}`
? ParamWithOptionalRegExp extends `${infer Param}(${infer _RegExp})`
? ExtractRouteOptionalParam<Param> & ExtractRouteParams<Rest>
: ExtractRouteOptionalParam<ParamWithOptionalRegExp> & ExtractRouteParams<Rest>
: PathType extends `${infer _Start}:${infer ParamWithOptionalRegExp}`
? ParamWithOptionalRegExp extends `${infer Param}(${infer _RegExp})`
? ExtractRouteOptionalParam<Param>
: ExtractRouteOptionalParam<ParamWithOptionalRegExp>
: {};

/*
* Components: <Route />
Expand All @@ -60,15 +59,11 @@ export interface RouteProps<
RoutePath extends Path = Path
> {
children?:
| ((
params: T extends DefaultParams ? T : ExtractRouteParams<RoutePath>
) => ReactNode)
| ((params: T extends DefaultParams ? T : ExtractRouteParams<RoutePath>) => ReactNode)
| ReactNode;
path?: RoutePath;
component?: ComponentType<
RouteComponentProps<
T extends DefaultParams ? T : ExtractRouteParams<RoutePath>
>
RouteComponentProps<T extends DefaultParams ? T : ExtractRouteParams<RoutePath>>
>;
}

Expand All @@ -93,10 +88,9 @@ export type LinkProps<H extends BaseLocationHook = LocationHook> = Omit<
> &
NavigationalProps<H>;

export type RedirectProps<H extends BaseLocationHook = LocationHook> =
NavigationalProps<H> & {
children?: never;
};
export type RedirectProps<H extends BaseLocationHook = LocationHook> = NavigationalProps<H> & {
children?: never;
};

export function Redirect<H extends BaseLocationHook = LocationHook>(
props: PropsWithChildren<RedirectProps<H>>,
Expand All @@ -114,7 +108,7 @@ export function Link<H extends BaseLocationHook = LocationHook>(

export interface SwitchProps {
location?: string;
children: Array<ReactElement<RouteProps>>;
children: ReactNode;
}
export const Switch: FunctionComponent<SwitchProps>;

Expand Down Expand Up @@ -142,12 +136,8 @@ export function useRouter(): RouterProps;
export function useRoute<
T extends DefaultParams | undefined = undefined,
RoutePath extends Path = Path
>(
pattern: RoutePath
): Match<T extends DefaultParams ? T : ExtractRouteParams<RoutePath>>;
>(pattern: RoutePath): Match<T extends DefaultParams ? T : ExtractRouteParams<RoutePath>>;

export function useLocation<
H extends BaseLocationHook = LocationHook
>(): HookReturnValue<H>;
export function useLocation<H extends BaseLocationHook = LocationHook>(): HookReturnValue<H>;

// tslint:enable:no-unnecessary-generics
36 changes: 20 additions & 16 deletions types/ts4.1/type-specs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,19 +59,13 @@ const invalidParamsWithGeneric: Params<{ id: number }> = { id: 13 }; // $ExpectE
This is a <b>mixed</b> content
</Route>;

<Route path="/users/:id">
{(params: Params): React.ReactNode => `User id: ${params.id}`}
</Route>;
<Route path="/users/:id">{(params: Params): React.ReactNode => `User id: ${params.id}`}</Route>;

<Route path="/users/:id">{({ id }) => `User id: ${id}`}</Route>;

<Route path="/users/:id">
{({ age }) => `User age: ${age}`} // $ExpectError
</Route>;
<Route path="/users/:id">{({ age }) => `User age: ${age}`}</Route>; // $ExpectError

<Route path="/users/:id">
{({ age }: { age: string }) => `User age: ${age}`}
</Route>;
<Route path="/users/:id">{({ age }: { age: string }) => `User age: ${age}`}</Route>;

<Route path="/app" match={true} />; // $ExpectError

Expand All @@ -84,9 +78,7 @@ const invalidParamsWithGeneric: Params<{ id: number }> = { id: 13 }; // $ExpectE
</Route>;

// infer only named params
<Route path="/:first/:second">
{({ first, second }) => `first: ${first}, second: ${second}`}
</Route>;
<Route path="/:first/:second">{({ first, second }) => `first: ${first}, second: ${second}`}</Route>;

// for pathToRegexp matcher
<Route path="/:user([a-z]i+)/profile/:tab/:first+/:second*">
Expand Down Expand Up @@ -153,10 +145,7 @@ const invalidParamsWithGeneric: Params<{ id: number }> = { id: 13 }; // $ExpectE

Redirect<UseNetworkLocation>({ href: "/home", delay: 1000 });
// example custom hook
type UseLocWithNoOptions = () => [
string,
(to: string, foo: number, bar: string) => void
];
type UseLocWithNoOptions = () => [string, (to: string, foo: number, bar: string) => void];
Redirect<UseLocWithNoOptions>({ href: "/app" });

<Redirect>something</Redirect>; // $ExpectError
Expand All @@ -175,6 +164,21 @@ Redirect<UseLocWithNoOptions>({ href: "/app" });
<Route />
</Switch>;

<Switch>
This won't be rendered, but it's allowed
<Route path="/app/users" />
<>
<div />
I'm a fragment
</>
{false && <a>Conditionals</a>}
{null}
{undefined}
<Route />
</Switch>;

<Switch />; // $ExpectError

/*
* Router specs
*/
Expand Down

0 comments on commit 8f943ab

Please sign in to comment.