In my API, a request object is used to retrieve a list of records. If the request object includes the property inlineCount
, the API will return the data in the format
{ results: T[], inlineCount: number }
. Otherwise, it will simply return T[]
.
The code snippet below demonstrates this process:
interface InlineCountResult<T> {
results: T[];
inlineCount: number;
}
interface Options {
inlineCount?: boolean;
}
async function getApiResponse<T>(options: Options): T[] | InlineCountResult<T> {
return this.http.get(options);
}
async function getClients(options: Options) {
return getApiResponse<model.Client>(options); // Returns model.Client[] or InlineCountResult<model.Client>
}
The goal is to have two different types of responses based on whether inlineCount
is set or not:
let responseAsArray: model.Client[] = getClients();
let responseWithCount: InlineCountResult<model.Client> = getClients({ inlineCount: true });
However, both calls currently return the type
model.Client[] | InlineCountResult<model.Client>
, which requires unnecessary type casting for proper functionality. Although using as
casting is an option, it's preferable to avoid it.
To address this issue, one approach is to create a function that can determine the return type without requiring an extra argument.
Partial solution:
// Functions to normalize response types
export function normalizeArray<T>(result: T[] | InlineCountResult<T>): T[] {
return Array.isArray(result) ? result : result.results;
}
export function normalizeInlineCount<T>(result: T[] | InlineCountResult<T>) {
return Array.isArray(result) ? new InlineCountResult<T>(result, result.length) : result.inlineCount ? result : new InlineCountResult<T>([], 0);
}
// Request types
export interface ExecuteQueryRequest {
inlineCount?: boolean;
}
export type QueryResponseNormalizer<T, TResponse> = (response: (T[] | InlineCountResult<T>)) => TResponse;
// Query method handling
function executeQuery<T, TResponse>(request: ExecuteQueryRequest, normalizeResults: QueryResponseNormalizer<T, TResponse>): TResponse {
const items: T[] = []; // Example: Call HTTP service
return normalizeResults(items);
}
// One API for both scenarios
function getClients<TResponse>(request: ExecuteQueryRequest, normalizeResults: QueryResponseNormalizer<model.Client, TResponse>) {
let response = executeQuery(request as ExecuteQueryRequest, normalizeResults);
return response;
}
// Example calls
let responseA: InlineCountResult<model.Client> = getClients({ inlineCount: true }, normalizeInlineCount);
let responseB: model.Client[] = getClients({}, normalizeArray);