Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions types/clerk.io/clerk.io-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,19 @@ if (window.Clerk) {
facets: null,
status: "ok",
};

// Test that `attributes` narrows the keys of `product_data` in the response
window.Clerk("call", "search/search", {
key: "test-key",
query: "test",
limit: 5,
attributes: ["sku", "title"],
}, (response) => {
if (response.product_data) {
response.product_data[0].sku; // $ExpectType unknown
response.product_data[0].title; // $ExpectType unknown
// @ts-expect-error
response.product_data[0].description;
}
});
}
75 changes: 48 additions & 27 deletions types/clerk.io/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Minimum TypeScript Version: 5.0
import * as Config from "./types/config";
import * as Helpers from "./types/helpers";
import * as Response from "./types/response";
Expand Down Expand Up @@ -81,44 +82,64 @@ export interface ConfigTypes {
"recommendations/page/related_categories": Config.recommendationsPageRelatedCategoriesConfig;
}

export interface ResponseTypes {
"search/search": Response.searchSearchResponse;
"search/predictive": Response.searchPredictiveResponse;
export interface ResponseTypes<Attributes extends string[] = string[]> {
"search/search": Response.searchSearchResponse<Attributes>;
"search/predictive": Response.searchPredictiveResponse<Attributes>;
"search/suggestions": Response.searchSuggestionsResponse;
"search/categories": Response.searchCategoriesResponse;
"search/pages": Response.searchPagesResponse;
"search/popular": Response.searchPopularResponse;
"recommendations/popular": Response.recommendationsPopularResponse;
"recommendations/trending": Response.recommendationsTrendingResponse;
"recommendations/new": Response.recommendationsNewResponse;
"recommendations/currently_watched": Response.recommendationsCurrentlyWatchedResponse;
"recommendations/recently_bought": Response.recommendationsRecentlyBoughtResponse;
"recommendations/keywords": Response.recommendationsKeywordsResponse;
"recommendations/complementary": Response.recommendationsComplementaryResponse;
"recommendations/substituting": Response.recommendationsSubstitutingResponse;
"recommendations/most_sold_with": Response.recommendationsMostSoldWithResponse;
"recommendations/category/popular": Response.recommendationsCategoryPopularResponse;
"recommendations/category/trending": Response.recommendationsCategoryTrendingResponse;
"recommendations/category/new": Response.recommendationsCategoryNewResponse;
"recommendations/category/popular_subcategories": Response.recommendationsCategoryPopularSubcategoriesResponse;
"recommendations/visitor/history": Response.recommendationsVisitorHistoryResponse;
"recommendations/visitor/complementary": Response.recommendationsVisitorComplementaryResponse;
"recommendations/visitor/substituting": Response.recommendationsVisitorSubstitutingResponse;
"recommendations/customer/history": Response.recommendationsCustomerHistoryResponse;
"recommendations/customer/complementary": Response.recommendationsCustomerComplementaryResponse;
"recommendations/customer/substituting": Response.recommendationsCustomerSubstitutingResponse;
"recommendations/popular": Response.recommendationsPopularResponse<Attributes>;
"recommendations/trending": Response.recommendationsTrendingResponse<Attributes>;
"recommendations/new": Response.recommendationsNewResponse<Attributes>;
"recommendations/currently_watched": Response.recommendationsCurrentlyWatchedResponse<Attributes>;
"recommendations/recently_bought": Response.recommendationsRecentlyBoughtResponse<Attributes>;
"recommendations/keywords": Response.recommendationsKeywordsResponse<Attributes>;
"recommendations/complementary": Response.recommendationsComplementaryResponse<Attributes>;
"recommendations/substituting": Response.recommendationsSubstitutingResponse<Attributes>;
"recommendations/most_sold_with": Response.recommendationsMostSoldWithResponse<Attributes>;
"recommendations/category/popular": Response.recommendationsCategoryPopularResponse<Attributes>;
"recommendations/category/trending": Response.recommendationsCategoryTrendingResponse<Attributes>;
"recommendations/category/new": Response.recommendationsCategoryNewResponse<Attributes>;
"recommendations/category/popular_subcategories": Response.recommendationsCategoryPopularSubcategoriesResponse<
Attributes
>;
"recommendations/visitor/history": Response.recommendationsVisitorHistoryResponse<Attributes>;
"recommendations/visitor/complementary": Response.recommendationsVisitorComplementaryResponse<Attributes>;
"recommendations/visitor/substituting": Response.recommendationsVisitorSubstitutingResponse<Attributes>;
"recommendations/customer/history": Response.recommendationsCustomerHistoryResponse<Attributes>;
"recommendations/customer/complementary": Response.recommendationsCustomerComplementaryResponse<Attributes>;
"recommendations/customer/substituting": Response.recommendationsCustomerSubstitutingResponse<Attributes>;
"recommendations/page/substituting": Response.recommendationsPageSubstitutingResponse;
"recommendations/page/product": Response.recommendationsPageProductResponse;
"recommendations/page/category": Response.recommendationsPageCategoryResponse;
"recommendations/page/related_products": Response.recommendationsPageRelatedProductsResponse;
"recommendations/page/related_categories": Response.recommendationsPageRelatedCategoriesResponse;
"recommendations/page/product": Response.recommendationsPageProductResponse<Attributes>;
"recommendations/page/category": Response.recommendationsPageCategoryResponse<Attributes>;
"recommendations/page/related_products": Response.recommendationsPageRelatedProductsResponse<Attributes>;
"recommendations/page/related_categories": Response.recommendationsPageRelatedCategoriesResponse<Attributes>;
}

/**
* @description The subset of endpoints whose config accepts an `attributes` field.
*/
export type EndpointsWithAttributes = {
[K in ClerkEndpoints]: ConfigTypes[K] extends { attributes?: string[] } ? K : never;
}[ClerkEndpoints];

/**
* @see https://docs.clerk.io/docs/clerkjs-custom-api-calls
* @description Calls a Clerk.js endpoint, narrowing `product_data` in the response to the literal tuple `Attributes`
*/
export function Clerk<T extends EndpointsWithAttributes, const Attributes extends string[] = string[]>(
method: "call",
endpoint: T,
config: Omit<ConfigTypes[T], "attributes"> & { attributes?: Attributes },
callback?: (response: ResponseTypes<Attributes>[T]) => void,
error?: (error: ClerkErrorResponse) => void,
): void;
/**
* @see https://docs.clerk.io/docs/clerkjs-custom-api-calls
* @description Calls a Clerk.js endpoint
*/
export function Clerk<T extends ClerkEndpoints>(
export function Clerk<T extends Exclude<ClerkEndpoints, EndpointsWithAttributes>>(
method: "call",
endpoint: T,
config: ConfigTypes[T],
Expand Down
129 changes: 86 additions & 43 deletions types/clerk.io/types/response.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ export interface BaseResponse {
debug?: Record<string, unknown>;
}

export type BaseProductResponse = BaseResponse & {
product_data?: PickAttributes<string[]>[];
export type BaseProductResponse<Attributes extends string[] = string[]> = BaseResponse & {
product_data?: PickAttributes<Attributes>[];
};

export type BaseCountedResponse = BaseProductResponse & {
export type BaseCountedResponse<Attributes extends string[] = string[]> = BaseProductResponse<Attributes> & {
count: number;
facets: unknown | null;
};
Expand Down Expand Up @@ -44,13 +44,15 @@ export interface Page {
}

// Search responses
export type searchSearchResponse = BaseCountedResponse;
export type searchSearchResponse<Attributes extends string[] = string[]> = BaseCountedResponse<Attributes>;

export type searchPredictiveResponse = BaseProductResponse & {
facets: unknown | null;
hits: number;
query: string;
};
export type searchPredictiveResponse<Attributes extends string[] = string[]> =
& BaseProductResponse<Attributes>
& {
facets: unknown | null;
hits: number;
query: string;
};

export type searchSuggestionsResponse = BaseResponse;

Expand All @@ -66,50 +68,91 @@ export type searchPagesResponse = BaseResponse & {
export type searchPopularResponse = BaseResponse;

// Recommendations responses
export type recommendationsPopularResponse = BaseCountedResponse;
export type recommendationsTrendingResponse = BaseCountedResponse;
export type recommendationsNewResponse = BaseCountedResponse;
export type recommendationsCurrentlyWatchedResponse = BaseProductResponse;
export type recommendationsRecentlyBoughtResponse = BaseProductResponse;
export type recommendationsKeywordsResponse = BaseProductResponse;
export type recommendationsComplementaryResponse = BaseProductResponse;
export type recommendationsSubstitutingResponse = BaseProductResponse;
export type recommendationsMostSoldWithResponse = BaseCountedResponse;
export type recommendationsPopularResponse<Attributes extends string[] = string[]> = BaseCountedResponse<Attributes>;
export type recommendationsTrendingResponse<Attributes extends string[] = string[]> = BaseCountedResponse<
Attributes
>;
export type recommendationsNewResponse<Attributes extends string[] = string[]> = BaseCountedResponse<Attributes>;
export type recommendationsCurrentlyWatchedResponse<Attributes extends string[] = string[]> = BaseProductResponse<
Attributes
>;
export type recommendationsRecentlyBoughtResponse<Attributes extends string[] = string[]> = BaseProductResponse<
Attributes
>;
export type recommendationsKeywordsResponse<Attributes extends string[] = string[]> = BaseProductResponse<
Attributes
>;
export type recommendationsComplementaryResponse<Attributes extends string[] = string[]> = BaseProductResponse<
Attributes
>;
export type recommendationsSubstitutingResponse<Attributes extends string[] = string[]> = BaseProductResponse<
Attributes
>;
export type recommendationsMostSoldWithResponse<Attributes extends string[] = string[]> = BaseCountedResponse<
Attributes
>;

// Category endpoints
export type recommendationsCategoryPopularResponse = BaseCountedResponse;
export type recommendationsCategoryTrendingResponse = BaseProductResponse & {
count: number;
facets?: unknown | null;
};
export type recommendationsCategoryNewResponse = BaseCountedResponse;
export type recommendationsCategoryPopularSubcategoriesResponse = BaseCountedResponse;
export type recommendationsCategoryPopularResponse<Attributes extends string[] = string[]> = BaseCountedResponse<
Attributes
>;
export type recommendationsCategoryTrendingResponse<Attributes extends string[] = string[]> =
& BaseProductResponse<Attributes>
& {
count: number;
facets?: unknown | null;
};
export type recommendationsCategoryNewResponse<Attributes extends string[] = string[]> = BaseCountedResponse<
Attributes
>;
export type recommendationsCategoryPopularSubcategoriesResponse<Attributes extends string[] = string[]> =
BaseCountedResponse<Attributes>;

// Visitor endpoints
export type recommendationsVisitorHistoryResponse = BaseProductResponse;
export type recommendationsVisitorComplementaryResponse = BaseProductResponse;
export type recommendationsVisitorSubstitutingResponse = BaseProductResponse;
export type recommendationsVisitorHistoryResponse<Attributes extends string[] = string[]> = BaseProductResponse<
Attributes
>;
export type recommendationsVisitorComplementaryResponse<Attributes extends string[] = string[]> = BaseProductResponse<
Attributes
>;
export type recommendationsVisitorSubstitutingResponse<Attributes extends string[] = string[]> = BaseProductResponse<
Attributes
>;

// Customer endpoints
export type recommendationsCustomerHistoryResponse = BaseProductResponse;
export type recommendationsCustomerComplementaryResponse = BaseProductResponse;
export type recommendationsCustomerSubstitutingResponse = BaseProductResponse;
export type recommendationsCustomerHistoryResponse<Attributes extends string[] = string[]> = BaseProductResponse<
Attributes
>;
export type recommendationsCustomerComplementaryResponse<Attributes extends string[] = string[]> = BaseProductResponse<
Attributes
>;
export type recommendationsCustomerSubstitutingResponse<Attributes extends string[] = string[]> = BaseProductResponse<
Attributes
>;

// Page endpoints
export type recommendationsPageSubstitutingResponse = BaseResponse & {
pages: Page[];
product_data?: null[];
};

export type recommendationsPageProductResponse = BaseProductResponse & {
pages: Page[];
};

export type recommendationsPageCategoryResponse = BaseProductResponse & {
pages: Page[];
};

export type recommendationsPageRelatedProductsResponse = BaseCountedResponse;
export type recommendationsPageRelatedCategoriesResponse = BaseCountedResponse & {
categories: Category[];
};
export type recommendationsPageProductResponse<Attributes extends string[] = string[]> =
& BaseProductResponse<Attributes>
& {
pages: Page[];
};

export type recommendationsPageCategoryResponse<Attributes extends string[] = string[]> =
& BaseProductResponse<Attributes>
& {
pages: Page[];
};

export type recommendationsPageRelatedProductsResponse<Attributes extends string[] = string[]> = BaseCountedResponse<
Attributes
>;
export type recommendationsPageRelatedCategoriesResponse<Attributes extends string[] = string[]> =
& BaseCountedResponse<Attributes>
& {
categories: Category[];
};