我正在用TypeScript为API创建包装。
一个API调用可以返回两种类型的响应:基本响应和扩展响应,具体取决于参数属性。
如果arguments属性为true,则将扩展响应。如果不提供参数属性或为false,则响应将是基本的。
这是代码示例:
interface Args {
token: string;
region: string;
extendedResponse?: boolean;
}
interface BasicResponse {
ID: string;
}
interface ExtendedResponse extends BasicResponse {
extendedResponseProperty: object;
}
function returnResponse(args: Args): BasicResponse | ExtendedResponse {
if (args.extendedResponse) {
return { ID: "foobar", extendedResponseProperty: {} };
}
return {ID: "foobar"}
}
let res1 = returnResponse({ token: "asdf", region: "us", extendedResponse: true });
但是,此代码并未向该方法的使用者显示已返回ExtendedResponse
,而是将其显示为BasicResponse | ExtendedResponse
,并且在智能感知中未显示extendedResponseProperty
。
我最接近解决此问题的方法是使用以下代码:
interface Args {
token: string;
region: string;
}
interface ExtendedArgs extends Args {
extendedResponse: true;
}
interface BasicResponse {
ID: string;
}
interface ExtendedResponse extends BasicResponse {
extendedResponseProperty: object;
}
function returnResponse(args: Args): BasicResponse;
function returnResponse(args: ExtendedArgs): ExtendedResponse;
function returnResponse(args: Args | ExtendedArgs): BasicResponse | ExtendedResponse {
if (args as ExtendedArgs) {
return { ID: "foobar", extendedResponseProperty: {} };
}
return {ID: "foobar"}
}
let res1 = returnResponse({ token: "asdf", region: "us" });
let res2 = returnResponse({ token: "asdf", region: "asdf", extendedResponse: true });
但是我不太喜欢这种解决方案,因为此方法的使用者需要处理函数重载和两种类型的输入args。
只有一个输入Args,碰巧它可以具有extendedResponse: boolean
属性。两个输入参数使它不那么直观。
是否存在可以解决此问题的基于TypeScript类型的方法?
更新
一个更好的解决方案是对输入的Args使用联合类型:
interface Args {
token?: string;
region?: string;
}
interface BasicResponse {
ID: string;
}
interface ExtendedResponse extends BasicResponse {
extendedResponseProperty: object;
}
function returnResponse(args: Args): BasicResponse;
function returnResponse(args: Args & {callbackData: true}): ExtendedResponse;
function returnResponse(args: Args | Args & {callbackData: true}): BasicResponse | ExtendedResponse {
if (args as { callbackData: true }) {
return { ID: "foobar", extendedResponseProperty: {}}
}
return {ID: "foobar"}
}
let res1 = returnResponse({ token: "asdf", region: "us" });
let res2 = returnResponse({ token: "foo", callbackData: true });
对于消费者而言,它更具可读性,因为它表明添加callbackData: true
将返回不同的结果,但仍然不理想,因为它仍然是两种不同的参数类型。
答案 0 :(得分:0)
如果您不喜欢重载,则可以使用这样的泛型:
interface Args {
token: string;
region: string;
extendedResponse?: true;
}
interface BasicResponse {
ID: string;
}
interface ExtendedResponse extends BasicResponse {
extendedResponseProperty: object;
}
function returnResponse<A extends Args>(args: A): A extends { extendedResponse: true} ? ExtendedResponse : BasicResponse {
if (args.extendedResponse === true) {
return { ID: "foobar", extendedResponseProperty: {} } as any;
}
return {ID: "foobar"} as any
}
let res1 = returnResponse({ token: "asdf", region: "us" });
let res2 = returnResponse({ token: "asdf", region: "asdf", extendedResponse: true });
但是,老实说,我喜欢您codesandbox中的代码。它描述了在没有附加魔术的情况下会发生什么,并且易于阅读。
此外,我不确定您所说的“使用者必须处理方法重载”是什么意思-使用者获得了他们所调用的内容,或多或少。您在这里描述的是某种行为,无论您如何描述-不同的输入参数将导致不同的输出,并且只要您的运行时代码执行了此操作,使用者就必须处理该行为。