Skip to content

Commit d6a7ea6

Browse files
dimklLekoArts
andauthoredNov 29, 2023
chore(types,clerk-react): Make routerPush / routerReplace both required or both optional (#2227)
* chore(types): Use `Without` generic to resolve issues with `Omit` and complex types * chore(types): Make `routerPush` and `routerReplace` options both required or both optional In order to use unions we converted the `ClerkOptions` interface to type to compose the union for mutual required or optional for `routerPush` and `routerReplace` options. * chore(repo): Ignore test runner files and CHANGELOG.md from formatting Fixed the 3 slowed formatted files (filename ignored -> ms saved): - packages/backend/tests/cloudflare-miniflare/worker.js -> 1582ms - packages/backend/tests/edge-runtime/bundle.js -> 1477ms - packages/clerk-js/CHANGELOG.md -> 177ms * chore(repo): Add changeset * chore(repo): Update ui-retheme-changes-reminder to notify for files with retheme variant * Update .changeset/tiny-forks-sit.md Co-authored-by: Lennart <lekoarts@gmail.com> --------- Co-authored-by: Lennart <lekoarts@gmail.com>
1 parent c923867 commit d6a7ea6

File tree

7 files changed

+81
-27
lines changed

7 files changed

+81
-27
lines changed
 

‎.changeset/tiny-forks-sit.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@clerk/clerk-react': minor
3+
'@clerk/types': minor
4+
---
5+
6+
Update the TypeScript types of `<ClerkProvider />`. If you use the `routerPush` prop you're now required to also provide the `routerReplace` prop (or other way around). You can also not provide them at all since both props are optional.

‎.github/workflows/ui-retheme-changes-reminder.yml

+7-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@ on:
66
- main
77
paths:
88
- 'packages/clerk-js/src/ui/**'
9-
9+
# files with matching `*.retheme.ts` retheme variant
10+
- 'packages/localizations/src/en-US.ts'
11+
- 'packages/localizations/src/index.ts'
12+
- 'packages/types/src/appearance.ts'
13+
- 'packages/types/src/clerk.ts'
14+
- 'packages/types/src/index.ts'
15+
- 'packages/types/src/localization.ts'
1016
jobs:
1117
check-changes:
1218
runs-on: ubuntu-latest

‎.prettierignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,6 @@ dist
1515
examples
1616
node_modules
1717
package-lock.json
18-
playground
18+
playground
19+
packages/backend/tests/**/*.js
20+
/**/CHANGELOG.md

‎packages/react/src/contexts/__tests__/ClerkProvider.test.tsx

+19
Original file line numberDiff line numberDiff line change
@@ -224,4 +224,23 @@ describe('ClerkProvider', () => {
224224
expectTypeOf({ publishableKey: 'test' }).not.toMatchTypeOf<ClerkProviderProps>();
225225
});
226226
});
227+
228+
describe('navigation options', () => {
229+
it('expects both routerPush & routerReplace to pass', () => {
230+
expectTypeOf({
231+
publishableKey: 'test',
232+
children: '',
233+
routerPush: () => {},
234+
routerReplace: () => {},
235+
}).toMatchTypeOf<ClerkProviderProps>();
236+
});
237+
238+
it('errors if one of routerPush / routerReplace is passed', () => {
239+
expectTypeOf({
240+
publishableKey: 'test',
241+
children: '',
242+
routerPush: () => {},
243+
}).not.toMatchTypeOf<ClerkProviderProps>();
244+
});
245+
});
227246
});

‎packages/react/src/types.ts

+6-14
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type {
1111
SignInRedirectOptions,
1212
SignUpRedirectOptions,
1313
UserResource,
14+
Without,
1415
} from '@clerk/types';
1516
import type React from 'react';
1617

@@ -23,7 +24,7 @@ declare global {
2324
}
2425
}
2526

26-
export type IsomorphicClerkOptions = Omit<ClerkOptions, 'isSatellite'> & {
27+
export type IsomorphicClerkOptions = Without<ClerkOptions, 'isSatellite'> & {
2728
Clerk?: ClerkProp;
2829
clerkJSUrl?: string;
2930
clerkJSVariant?: 'headless' | '';
@@ -37,19 +38,10 @@ export type ClerkProviderProps = IsomorphicClerkOptions & {
3738
initialState?: InitialState;
3839
};
3940

40-
// TODO(@dimkl): replacing it with the following make nextjs type tests fail
41-
// `Exclude<IsomorphicClerkOptions, 'publishableKey'> & { publishableKey?: string }`
42-
// find another way to reduce the duplication.
43-
export type ClerkProviderOptionsWrapper = Omit<ClerkOptions, 'isSatellite'> & {
44-
Clerk?: ClerkProp;
45-
clerkJSUrl?: string;
46-
clerkJSVariant?: 'headless' | '';
47-
clerkJSVersion?: string;
48-
sdkMetadata?: SDKMetadata;
41+
export type ClerkProviderOptionsWrapper = Without<IsomorphicClerkOptions, 'publishableKey'> & {
4942
publishableKey?: string;
50-
} & MultiDomainAndOrProxy & {
51-
children: React.ReactNode;
52-
};
43+
children: React.ReactNode;
44+
};
5345

5446
export interface BrowserClerkConstructor {
5547
new (publishableKey: string, options?: DomainOrProxyUrl): BrowserClerk;
@@ -75,7 +67,7 @@ export interface MountProps {
7567
}
7668

7769
export interface HeadlessBrowserClerk extends Clerk {
78-
load: (opts?: Omit<ClerkOptions, 'isSatellite'>) => Promise<void>;
70+
load: (opts?: Without<ClerkOptions, 'isSatellite'>) => Promise<void>;
7971
updateClient: (client: ClientResource) => void;
8072
}
8173

‎packages/types/src/clerk.retheme.ts

+20-2
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,25 @@ export type CustomNavigation = (to: string, options?: NavigateOptions) => Promis
495495

496496
export type ClerkThemeOptions = DeepSnakeToCamel<DeepPartial<DisplayThemeJSON>>;
497497

498-
export interface ClerkOptions {
498+
/**
499+
* Navigation options used to replace or push history changes.
500+
* Both `routerPush` & `routerReplace` OR none options should be passed.
501+
*/
502+
type ClerkOptionsNavigationFn =
503+
| {
504+
routerPush?: never;
505+
routerReplace?: never;
506+
}
507+
| {
508+
routerPush: (to: string) => Promise<unknown> | unknown;
509+
routerReplace: (to: string) => Promise<unknown> | unknown;
510+
};
511+
512+
type ClerkOptionsNavigation = ClerkOptionsNavigationFn & {
513+
routerDebug?: boolean;
514+
};
515+
516+
export type ClerkOptions = ClerkOptionsNavigation & {
499517
appearance?: Appearance;
500518
localization?: LocalizationResource;
501519
/**
@@ -535,7 +553,7 @@ export interface ClerkOptions {
535553
};
536554

537555
sdkMetadata?: SDKMetadata;
538-
}
556+
};
539557

540558
export interface NavigateOptions {
541559
replace?: boolean;

‎packages/types/src/clerk.ts

+20-9
Original file line numberDiff line numberDiff line change
@@ -495,16 +495,27 @@ export type CustomNavigation = (to: string, options?: NavigateOptions) => Promis
495495

496496
export type ClerkThemeOptions = DeepSnakeToCamel<DeepPartial<DisplayThemeJSON>>;
497497

498-
export interface ClerkOptions {
499-
appearance?: Appearance;
500-
localization?: LocalizationResource;
501-
/**
502-
* Navigation
503-
*/
504-
routerPush?: (to: string) => Promise<unknown> | unknown;
505-
routerReplace?: (to: string) => Promise<unknown> | unknown;
498+
/**
499+
* Navigation options used to replace or push history changes.
500+
* Both `routerPush` & `routerReplace` OR none options should be passed.
501+
*/
502+
type ClerkOptionsNavigationFn =
503+
| {
504+
routerPush?: never;
505+
routerReplace?: never;
506+
}
507+
| {
508+
routerPush: (to: string) => Promise<unknown> | unknown;
509+
routerReplace: (to: string) => Promise<unknown> | unknown;
510+
};
511+
512+
type ClerkOptionsNavigation = ClerkOptionsNavigationFn & {
506513
routerDebug?: boolean;
514+
};
507515

516+
export type ClerkOptions = ClerkOptionsNavigation & {
517+
appearance?: Appearance;
518+
localization?: LocalizationResource;
508519
polling?: boolean;
509520
selectInitialSession?: (client: ClientResource) => ActiveSessionResource | null;
510521
/** Controls if ClerkJS will load with the standard browser setup using Clerk cookies */
@@ -536,7 +547,7 @@ export interface ClerkOptions {
536547
};
537548

538549
sdkMetadata?: SDKMetadata;
539-
}
550+
};
540551

541552
export interface NavigateOptions {
542553
replace?: boolean;

0 commit comments

Comments
 (0)
Please sign in to comment.