Skip to content

Commit

Permalink
A compromise solution to make ComponentProps work with typeof Link.
Browse files Browse the repository at this point in the history
  • Loading branch information
molefrog committed Dec 18, 2023
1 parent 923af29 commit a3c62bf
Show file tree
Hide file tree
Showing 3 changed files with 13 additions and 17 deletions.
9 changes: 6 additions & 3 deletions packages/wouter/test/link.test-d.tsx
Expand Up @@ -191,10 +191,13 @@ describe("<Link /> with `asChild` prop", () => {
</Link>;
});

it.skip("should work with `ComponentProps`", () => {
it("should work with `ComponentProps`", () => {
type LinkComponentProps = React.ComponentProps<typeof Link>;

// @ts-expect-error FIXME
expectTypeOf<LinkComponentProps>().toEqualTypeOf<LinkProps>();
// Because Link is a generic component, the props
// cant't contain navigation options of the default generic
// parameter `BrowserLocationHook`.
// So the best we can get are the props such as `href` etc.
expectTypeOf<LinkComponentProps>().toMatchTypeOf<LinkProps>();
});
});
12 changes: 3 additions & 9 deletions packages/wouter/test/location-hook.test-d.ts
@@ -1,9 +1,5 @@
import { it, expectTypeOf, describe } from "vitest";
import {
BaseLocationHook,
HookNavigationOptions,
HookReturnValue,
} from "wouter";
import { BaseLocationHook, HookNavigationOptions } from "wouter";

describe("`HookNavigationOptions` utility type", () => {
it("should return empty interface for hooks with no nav options", () => {
Expand Down Expand Up @@ -63,10 +59,8 @@ describe("`HookNavigationOptions` utility type", () => {
type C = HookNavigationOptions<() => []>;
});

it("should return arbitrary object when `BaseLocationHook` is given", () => {
it("should return empty object when `BaseLocationHook` is given", () => {
type Options = HookNavigationOptions<BaseLocationHook>;

const opts: Options = { a: 1, b: 2 };
opts.anything = 1;
expectTypeOf<Options>().toEqualTypeOf<{}>();
});
});
9 changes: 4 additions & 5 deletions packages/wouter/types/location-hook.d.ts
Expand Up @@ -21,16 +21,15 @@ export type BaseSearchHook = (...args: any[]) => SearchString;
// Returns the type of the location tuple of the given hook.
export type HookReturnValue<H extends BaseLocationHook> = ReturnType<H>;

// Utility type that allows us to distinguish between hooks that
// don't receive any options and `BaseLocationHook` that can accept anything
type ArbitraryObjectWhenNever<T> = 0 extends 1 & T
? { [key: string]: any }
// Utility type that allows us to handle cases like `any` and `never`
type EmptyInterfaceWhenAnyOrNever<T> = 0 extends 1 & T
? {}
: [T] extends [never]
? {}
: T;

// Returns the type of the navigation options that hook's push function accepts.
export type HookNavigationOptions<H extends BaseLocationHook> =
ArbitraryObjectWhenNever<
EmptyInterfaceWhenAnyOrNever<
NonNullable<Parameters<HookReturnValue<H>[1]>[1]> // get's the second argument of a tuple returned by the hook
>;

0 comments on commit a3c62bf

Please sign in to comment.