![](/img/trans.png)
[英]Can TypeScript infer a function return type based on an argument in this scenario?
[英]Can typescript infer a function's response type based on parameter values?
我有一个 API 请求 object 以获取记录列表。 如果在请求inlineCount
上设置了 inlineCount,则 API 返回{ results: T[], inlineCount: number }
。 否则,返回T[]
。
这看起来像:
interface inlineCountResult<T> {
results: T[];
inlineCount: number;
}
interface Options {
inlineCount?: boolean;
}
async getApiResponse<T>(options: Options): T[] | inlineCountResult<T> {
return this.http.get(options);
}
async getClients(options: Options) {
return this.getApiResponse<model.Client>(options);
}
这是我希望发生的事情:
let responseAsArray: model.Client[] = getClients();
let responseWithCount: inlineCountResult<model.Client> = getClients({ inlineCount: true });
但是,这两个调用的类型都是model.Client[] | inlineCountResult<model.Client>
model.Client[] | inlineCountResult<model.Client>
这使我的代码需要不必要的类型转换才能正常工作。 我知道我可以执行以下操作,但我想避免as
强制转换:
const clients: Client[] = await (getClients(clientQueryOptions) as Promise<Client[]>);
我的一个想法是编写一个 function 它将返回一种返回类型作为参数之一,但我想在不需要额外参数的情况下过关。
部分解决方案:
//
// 2 "normalize" functions each with a single return type
//
export function normalizeArray<T>(result: T[] | inlineCountResult<T>): T[] {
return Array.isArray(result) ? result : result.results;
}
export function normalizeInlineCount = function normalizeInlineCount<T>(result: T[] | inlineCountResult<T>) {
return Array.isArray(result) ? new inlineCountResult<T>(result, result.length) : result.inlineCount ? result : new inlineCountResult<T>([], 0);
}
//
// Types for request
//
export interface ExecuteQueryRequest {
inlineCount?: boolean;
}
export type QueryResponseNormalizer<T, TResponse> = (response: (T[] | inlineCountResult<T>)) => TResponse;
//
// Query method to handle
//
function executeQuery<T, TResponse>(request: ExecuteQueryRequest, normalizeResults: QueryResponseNormalizer<T, TResponse>): TResponse {
const items: T[] = []; // eg 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);
对于条件类型,结果类型可能取决于参数中是否存在inlineCount
,并且还可以从规范化 function 推断结果数组的类型。 为简单起见,忽略承诺,您可以定义这些帮助器类型:
interface InlineCountResult<T> {
results: T[]
inlineCount: number
}
interface InlineCountOption {
inlineCount: boolean
}
type PlainNormalizer<T> = (res: T[]) => T[]
type InlineCountNormalizer<T> = (res: InlineCountResult<T>) => InlineCountResult<T>
type Normalizer<O, T> =
O extends InlineCountOption ? InlineCountNormalizer<T> : PlainNormalizer<T>
type Result<O, T> = O extends InlineCountOption ? InlineCountResult<T> : T[]
并像这样输入getClients
:
function getClients<O, T>(options: O, normalizer: Normalizer<O, T>): Result<O, T> {
return {} as any
}
如果您现在定义两个像这样的虚拟归一化器
const normalizeInlineCount: InlineCountNormalizer<number> = {} as any
const normalizeArray: PlainNormalizer<string> = {} as any
响应的类型将被正确推断,错误类型的规范器将产生类型错误:
const responseA = getClients({ inlineCount: true }, normalizeInlineCount)
const responseB = getClients({}, normalizeArray)
const responseC = getClients({ inlineCount: true }, normalizeArray) // type error
const responseD = getClients({}, normalizeInlineCount) // type error
您需要对其进行一些调整以考虑承诺,以及除inlineCount
之外的其他选项属性,但这应该让您对解决方案有一个粗略的了解。
Oblosys 的回答帮助我找到了一个解决方案,该解决方案允许我的理想/简单 API 表面,而不需要我的调用者中的任何额外参数/类型,除了 2 个不同的Options
与InlineCountOptions
类型有和没有inlineCount
布尔:
// model.Client[]
const clients = await getClients();
// or
const clients2 = await getClients({ sort: "clientName" });
// InlineCountResult<model.Client>
const response = await getClients({ inlineCount: true });
为来自 API 的数据定义实体
namespace model {
export interface Client {
clientName: string;
}
}
定义请求/响应接口
interface Options {
filter?: any;
skip?: number;
page?: number;
sort?: string;
}
interface InlineCountOption extends Options {
inlineCount: boolean
}
interface InlineCountResult<T> {
results: T[]
inlineCount: number
}
type Option<O extends Options> = O extends InlineCountOption ? InlineCountOption : Options;
function queryOptions<O>(options: O): Option<O> {
return options as any;
}
通用 API 调用者,例如fetch
type Result<O, T> = O extends InlineCountOption ? InlineCountResult<T> : T[]
async function hitApi<O, T>(url: string, options: O): Promise<Result<O, T>> {
let data: unknown = []; // ex
return data as Result<O, T>;
}
特定于每个实体/模型
function getClients<O>(options: O): Promise<Result<O, model.Client>> {
return hitApi('getClients', options);
}
数组请求
const clientQueryOptions = queryOptions({});
// model.Client[]
const clients = await getClients(clientQueryOptions);
const clientNames = clients.map(x => x.clientName);
内联计数请求
const clientQueryOptions = queryOptions({ inlineCount: true });
// InlineCountResult<model.Client>
const clientsResponse = await getClients(clientQueryOptions);
const numClients = clientsResponse.inlineCount;
const clients = clientsResponse.results;
const clientNames = clients.map(x => x.clientName);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.