Skip to content

Commit f77e8cd

Browse files
authoredNov 14, 2023
refactor(clerk-js,nextjs,types): Add Autocomplete generic [SDK-900] (#2132)
1 parent 4144f25 commit f77e8cd

File tree

9 files changed

+50
-34
lines changed

9 files changed

+50
-34
lines changed
 

‎.changeset/old-actors-beg.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@clerk/clerk-js': patch
3+
'@clerk/nextjs': patch
4+
'@clerk/types': patch
5+
---
6+
7+
Add Autocomplete TS generic for union literals

‎packages/clerk-js/src/core/resources/OrganizationMembership.ts

+19-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type {
2+
Autocomplete,
23
ClerkPaginatedResponse,
34
ClerkResourceReloadParams,
45
GetUserOrganizationMembershipParams,
@@ -20,9 +21,7 @@ export class OrganizationMembership extends BaseResource implements Organization
2021
/**
2122
* @experimental The property is experimental and subject to change in future releases.
2223
*/
23-
// Adding (string & {}) allows for getting eslint autocomplete but also accepts any string
24-
// eslint-disable-next-line
25-
permissions: (OrganizationPermission | (string & {}))[] = [];
24+
permissions: Autocomplete<OrganizationPermission>[] = [];
2625
role!: MembershipRole;
2726
createdAt!: Date;
2827
updatedAt!: Date;
@@ -41,8 +40,16 @@ export class OrganizationMembership extends BaseResource implements Organization
4140
search: convertPageToOffset({ ...retrieveMembershipsParams, paginated: true }) as any,
4241
})
4342
.then(res => {
43+
if (!res?.response) {
44+
return {
45+
total_count: 0,
46+
data: [],
47+
};
48+
}
49+
50+
// TODO: Fix typing
4451
const { data: suggestions, total_count } =
45-
res?.response as unknown as ClerkPaginatedResponse<OrganizationMembershipJSON>;
52+
res.response as unknown as ClerkPaginatedResponse<OrganizationMembershipJSON>;
4653

4754
return {
4855
total_count,
@@ -99,9 +106,16 @@ export class OrganizationMembership extends BaseResource implements Organization
99106
},
100107
{ forceUpdateClient: true },
101108
);
102-
const currentMembership = (json?.response as unknown as OrganizationMembershipJSON[]).find(
109+
110+
if (!json?.response) {
111+
return this.fromJSON(null);
112+
}
113+
114+
// TODO: Fix typing
115+
const currentMembership = (json.response as unknown as OrganizationMembershipJSON[]).find(
103116
orgMem => orgMem.id === this.id,
104117
);
118+
105119
return this.fromJSON(currentMembership as OrganizationMembershipJSON);
106120
}
107121
}

‎packages/nextjs/src/server/authMiddleware.ts

+4-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { AuthObject, RequestState } from '@clerk/backend';
22
import { buildRequestUrl, constants } from '@clerk/backend';
33
import { isDevelopmentFromApiKey } from '@clerk/shared/keys';
4+
import type { Autocomplete } from '@clerk/types';
45
import type Link from 'next/link';
56
import type { NextFetchEvent, NextMiddleware, NextRequest } from 'next/server';
67
import { NextResponse } from 'next/server';
@@ -32,15 +33,9 @@ type NextTypedRoute<T = Parameters<typeof Link>['0']['href']> = T extends string
3233
// For extra safety, we won't recommend using a `/(.*)` route matcher.
3334
type ExcludeRootPath<T> = T extends '/' ? never : T;
3435

35-
// We want to show suggestions but also allow for free-text input
36-
// the (string & {}) type prevents the TS compiler from merging the typed union with the string type
37-
// https://github.com/Microsoft/TypeScript/issues/29729#issuecomment-505826972
38-
type RouteMatcherWithNextTypedRoutes =
39-
| WithPathPatternWildcard<ExcludeRootPath<NextTypedRoute>>
40-
| NextTypedRoute
41-
// This is necessary to allow all string, using something other than `{}` here WILL break types!
42-
// eslint-disable-next-line @typescript-eslint/ban-types
43-
| (string & {});
36+
type RouteMatcherWithNextTypedRoutes = Autocomplete<
37+
WithPathPatternWildcard<ExcludeRootPath<NextTypedRoute>> | NextTypedRoute
38+
>;
4439

4540
const INFINITE_REDIRECTION_LOOP_COOKIE = '__clerk_redirection_loop';
4641

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import type { OrganizationResource } from './organization';
1818
import type { MembershipRole } from './organizationMembership';
1919
import type { ActiveSessionResource } from './session';
2020
import type { UserResource } from './user';
21-
import type { DeepPartial, DeepSnakeToCamel } from './utils';
21+
import type { Autocomplete, DeepPartial, DeepSnakeToCamel } from './utils';
2222

2323
export type InstanceType = 'production' | 'development';
2424

@@ -805,7 +805,7 @@ type PrimitiveKeys<T> = {
805805
[K in keyof T]: T[K] extends string | boolean | number | null ? K : never;
806806
}[keyof T];
807807

808-
type LooseExtractedParams<T extends string> = `:${T}` | (string & NonNullable<unknown>);
808+
type LooseExtractedParams<T extends string> = Autocomplete<`:${T}`>;
809809

810810
export type OrganizationSwitcherProps = {
811811
/**

‎packages/types/src/clerk.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import type { OrganizationResource } from './organization';
1818
import type { MembershipRole } from './organizationMembership';
1919
import type { ActiveSessionResource } from './session';
2020
import type { UserResource } from './user';
21-
import type { DeepPartial, DeepSnakeToCamel } from './utils';
21+
import type { Autocomplete, DeepPartial, DeepSnakeToCamel } from './utils';
2222

2323
export type InstanceType = 'production' | 'development';
2424

@@ -805,7 +805,7 @@ type PrimitiveKeys<T> = {
805805
[K in keyof T]: T[K] extends string | boolean | number | null ? K : never;
806806
}[keyof T];
807807

808-
type LooseExtractedParams<T extends string> = `:${T}` | (string & NonNullable<unknown>);
808+
type LooseExtractedParams<T extends string> = Autocomplete<`:${T}`>;
809809

810810
export type OrganizationSwitcherProps = {
811811
/**

‎packages/types/src/json.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import type { SignUpField, SignUpIdentificationField, SignUpStatus } from './sig
1717
import type { OAuthStrategy } from './strategies';
1818
import type { BoxShadow, Color, EmUnit, FontWeight, HexColor } from './theme';
1919
import type { UserSettingsJSON } from './userSettings';
20-
import type { CamelToSnake } from './utils';
20+
import type { Autocomplete, CamelToSnake } from './utils';
2121
import type { VerificationStatus } from './verification';
2222

2323
export interface ClerkResourceJSON {
@@ -303,9 +303,7 @@ export interface OrganizationMembershipJSON extends ClerkResourceJSON {
303303
/**
304304
* @experimental The property is experimental and subject to change in future releases.
305305
*/
306-
// Adding (string & {}) allows for getting eslint autocomplete but also accepts any string
307-
// eslint-disable-next-line
308-
permissions: (OrganizationPermission | (string & {}))[];
306+
permissions: Autocomplete<OrganizationPermission>[];
309307
public_metadata: OrganizationMembershipPublicMetadata;
310308
public_user_data: PublicUserDataJSON;
311309
role: MembershipRole;

‎packages/types/src/organizationMembership.ts

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { Autocomplete } from 'utils';
2+
13
import type { OrganizationResource } from './organization';
24
import type { ClerkResource } from './resource';
35
import type { PublicUserData } from './session';
@@ -28,9 +30,7 @@ export interface OrganizationMembershipResource extends ClerkResource {
2830
/**
2931
* @experimental The property is experimental and subject to change in future releases.
3032
*/
31-
// Adding (string & {}) allows for getting eslint autocomplete but also accepts any string
32-
// eslint-disable-next-line
33-
permissions: (OrganizationPermission | (string & {}))[];
33+
permissions: Autocomplete<OrganizationPermission>[];
3434
publicMetadata: OrganizationMembershipPublicMetadata;
3535
publicUserData: PublicUserData;
3636
role: MembershipRole;
@@ -40,9 +40,7 @@ export interface OrganizationMembershipResource extends ClerkResource {
4040
update: (updateParams: UpdateOrganizationMembershipParams) => Promise<OrganizationMembershipResource>;
4141
}
4242

43-
// Adding (string & {}) allows for getting eslint autocomplete but also accepts any string
44-
// eslint-disable-next-line
45-
export type MembershipRole = 'admin' | 'basic_member' | 'guest_member' | (string & {});
43+
export type MembershipRole = Autocomplete<'admin' | 'basic_member' | 'guest_member'>;
4644

4745
export type OrganizationPermission =
4846
| 'org:sys_domains:manage'

‎packages/types/src/session.ts

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { Autocomplete } from 'utils';
2+
13
import type { ActJWTClaim } from './jwt';
24
import type { OrganizationPermission } from './organizationMembership';
35
import type { ClerkResource } from './resource';
@@ -31,9 +33,7 @@ type CheckAuthorizationParams =
3133
}
3234
| {
3335
role?: never;
34-
// Adding (string & {}) allows for getting eslint autocomplete but also accepts any string
35-
// eslint-disable-next-line
36-
permission: OrganizationPermission | (string & {});
36+
permission: Autocomplete<OrganizationPermission>;
3737
}
3838
)[];
3939
role?: never;
@@ -47,9 +47,7 @@ type CheckAuthorizationParams =
4747
| {
4848
some?: never;
4949
role?: never;
50-
// Adding (string & {}) allows for getting eslint autocomplete but also accepts any string
51-
// eslint-disable-next-line
52-
permission: OrganizationPermission | (string & {});
50+
permission: Autocomplete<OrganizationPermission>;
5351
};
5452

5553
export interface SessionResource extends ClerkResource {

‎packages/types/src/utils.ts

+6
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,9 @@ type IsSerializable<T> = T extends Function ? false : true;
8383
export type Serializable<T> = {
8484
[K in keyof T as IsSerializable<T[K]> extends true ? K : never]: T[K];
8585
};
86+
87+
/**
88+
* Enables autocompletion for a union type, while keeping the ability to use any string
89+
* or type of `T`
90+
*/
91+
export type Autocomplete<U extends T, T = string> = U | (T & Record<never, never>);

0 commit comments

Comments
 (0)
Please sign in to comment.