Skip to content

Commit

Permalink
refactor(clerk-js,nextjs,types): Add Autocomplete generic [SDK-900] (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
tmilewski authored Nov 14, 2023
1 parent 4144f25 commit f77e8cd
Show file tree
Hide file tree
Showing 9 changed files with 50 additions and 34 deletions.
7 changes: 7 additions & 0 deletions .changeset/old-actors-beg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@clerk/clerk-js': patch
'@clerk/nextjs': patch
'@clerk/types': patch
---

Add Autocomplete TS generic for union literals
24 changes: 19 additions & 5 deletions packages/clerk-js/src/core/resources/OrganizationMembership.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type {
Autocomplete,
ClerkPaginatedResponse,
ClerkResourceReloadParams,
GetUserOrganizationMembershipParams,
Expand All @@ -20,9 +21,7 @@ export class OrganizationMembership extends BaseResource implements Organization
/**
* @experimental The property is experimental and subject to change in future releases.
*/
// Adding (string & {}) allows for getting eslint autocomplete but also accepts any string
// eslint-disable-next-line
permissions: (OrganizationPermission | (string & {}))[] = [];
permissions: Autocomplete<OrganizationPermission>[] = [];
role!: MembershipRole;
createdAt!: Date;
updatedAt!: Date;
Expand All @@ -41,8 +40,16 @@ export class OrganizationMembership extends BaseResource implements Organization
search: convertPageToOffset({ ...retrieveMembershipsParams, paginated: true }) as any,
})
.then(res => {
if (!res?.response) {
return {
total_count: 0,
data: [],
};
}

// TODO: Fix typing
const { data: suggestions, total_count } =
res?.response as unknown as ClerkPaginatedResponse<OrganizationMembershipJSON>;
res.response as unknown as ClerkPaginatedResponse<OrganizationMembershipJSON>;

return {
total_count,
Expand Down Expand Up @@ -99,9 +106,16 @@ export class OrganizationMembership extends BaseResource implements Organization
},
{ forceUpdateClient: true },
);
const currentMembership = (json?.response as unknown as OrganizationMembershipJSON[]).find(

if (!json?.response) {
return this.fromJSON(null);
}

// TODO: Fix typing
const currentMembership = (json.response as unknown as OrganizationMembershipJSON[]).find(
orgMem => orgMem.id === this.id,
);

return this.fromJSON(currentMembership as OrganizationMembershipJSON);
}
}
Expand Down
13 changes: 4 additions & 9 deletions packages/nextjs/src/server/authMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { AuthObject, RequestState } from '@clerk/backend';
import { buildRequestUrl, constants } from '@clerk/backend';
import { isDevelopmentFromApiKey } from '@clerk/shared/keys';
import type { Autocomplete } from '@clerk/types';
import type Link from 'next/link';
import type { NextFetchEvent, NextMiddleware, NextRequest } from 'next/server';
import { NextResponse } from 'next/server';
Expand Down Expand Up @@ -32,15 +33,9 @@ type NextTypedRoute<T = Parameters<typeof Link>['0']['href']> = T extends string
// For extra safety, we won't recommend using a `/(.*)` route matcher.
type ExcludeRootPath<T> = T extends '/' ? never : T;

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

const INFINITE_REDIRECTION_LOOP_COOKIE = '__clerk_redirection_loop';

Expand Down
4 changes: 2 additions & 2 deletions packages/types/src/clerk.retheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import type { OrganizationResource } from './organization';
import type { MembershipRole } from './organizationMembership';
import type { ActiveSessionResource } from './session';
import type { UserResource } from './user';
import type { DeepPartial, DeepSnakeToCamel } from './utils';
import type { Autocomplete, DeepPartial, DeepSnakeToCamel } from './utils';

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

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

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

export type OrganizationSwitcherProps = {
/**
Expand Down
4 changes: 2 additions & 2 deletions packages/types/src/clerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import type { OrganizationResource } from './organization';
import type { MembershipRole } from './organizationMembership';
import type { ActiveSessionResource } from './session';
import type { UserResource } from './user';
import type { DeepPartial, DeepSnakeToCamel } from './utils';
import type { Autocomplete, DeepPartial, DeepSnakeToCamel } from './utils';

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

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

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

export type OrganizationSwitcherProps = {
/**
Expand Down
6 changes: 2 additions & 4 deletions packages/types/src/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import type { SignUpField, SignUpIdentificationField, SignUpStatus } from './sig
import type { OAuthStrategy } from './strategies';
import type { BoxShadow, Color, EmUnit, FontWeight, HexColor } from './theme';
import type { UserSettingsJSON } from './userSettings';
import type { CamelToSnake } from './utils';
import type { Autocomplete, CamelToSnake } from './utils';
import type { VerificationStatus } from './verification';

export interface ClerkResourceJSON {
Expand Down Expand Up @@ -303,9 +303,7 @@ export interface OrganizationMembershipJSON extends ClerkResourceJSON {
/**
* @experimental The property is experimental and subject to change in future releases.
*/
// Adding (string & {}) allows for getting eslint autocomplete but also accepts any string
// eslint-disable-next-line
permissions: (OrganizationPermission | (string & {}))[];
permissions: Autocomplete<OrganizationPermission>[];
public_metadata: OrganizationMembershipPublicMetadata;
public_user_data: PublicUserDataJSON;
role: MembershipRole;
Expand Down
10 changes: 4 additions & 6 deletions packages/types/src/organizationMembership.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { Autocomplete } from 'utils';

import type { OrganizationResource } from './organization';
import type { ClerkResource } from './resource';
import type { PublicUserData } from './session';
Expand Down Expand Up @@ -28,9 +30,7 @@ export interface OrganizationMembershipResource extends ClerkResource {
/**
* @experimental The property is experimental and subject to change in future releases.
*/
// Adding (string & {}) allows for getting eslint autocomplete but also accepts any string
// eslint-disable-next-line
permissions: (OrganizationPermission | (string & {}))[];
permissions: Autocomplete<OrganizationPermission>[];
publicMetadata: OrganizationMembershipPublicMetadata;
publicUserData: PublicUserData;
role: MembershipRole;
Expand All @@ -40,9 +40,7 @@ export interface OrganizationMembershipResource extends ClerkResource {
update: (updateParams: UpdateOrganizationMembershipParams) => Promise<OrganizationMembershipResource>;
}

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

export type OrganizationPermission =
| 'org:sys_domains:manage'
Expand Down
10 changes: 4 additions & 6 deletions packages/types/src/session.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { Autocomplete } from 'utils';

import type { ActJWTClaim } from './jwt';
import type { OrganizationPermission } from './organizationMembership';
import type { ClerkResource } from './resource';
Expand Down Expand Up @@ -31,9 +33,7 @@ type CheckAuthorizationParams =
}
| {
role?: never;
// Adding (string & {}) allows for getting eslint autocomplete but also accepts any string
// eslint-disable-next-line
permission: OrganizationPermission | (string & {});
permission: Autocomplete<OrganizationPermission>;
}
)[];
role?: never;
Expand All @@ -47,9 +47,7 @@ type CheckAuthorizationParams =
| {
some?: never;
role?: never;
// Adding (string & {}) allows for getting eslint autocomplete but also accepts any string
// eslint-disable-next-line
permission: OrganizationPermission | (string & {});
permission: Autocomplete<OrganizationPermission>;
};

export interface SessionResource extends ClerkResource {
Expand Down
6 changes: 6 additions & 0 deletions packages/types/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,9 @@ type IsSerializable<T> = T extends Function ? false : true;
export type Serializable<T> = {
[K in keyof T as IsSerializable<T[K]> extends true ? K : never]: T[K];
};

/**
* Enables autocompletion for a union type, while keeping the ability to use any string
* or type of `T`
*/
export type Autocomplete<U extends T, T = string> = U | (T & Record<never, never>);

0 comments on commit f77e8cd

Please sign in to comment.