typescript - 取决于函数参数属性的函数返回类型
问题描述
我正在为 TypeScript 中的 API 创建一个包装器。
一个 API 调用可以返回 2 种类型的响应:基本和扩展,具体取决于参数属性。
如果参数属性为真,则响应将被扩展。如果未提供参数属性或为 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
返回的 an,而是将类型显示为BasicResponse | ExtendedResponse
并且不显示extendedResponseProperty
intellisense。
我最接近解决这个问题的是使用以下代码:
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 });
但我不太喜欢这种解决方案,因为这种方法的使用者需要处理函数重载和两种类型的输入参数。
只有一个输入参数,它恰好可以具有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
将返回不同的结果,但仍然不理想,因为它仍然是两种不同的参数类型。
解决方案
如果您不喜欢重载,可以使用这样的泛型:
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 });
但老实说,我喜欢你的代码盒中的代码。它描述了在没有额外魔法的情况下会发生什么,并且易于阅读。
另外,我不确定您所说的“消费者将不得不处理方法重载”是什么意思——消费者得到了他们正在调用的东西,而不是或多或少。您在这里描述了某种行为,无论您如何描述 - 不同的输入参数将导致不同的输出,并且一旦您的运行时代码这样做,消费者无论如何都必须处理它。
推荐阅读
- ubuntu - Ubuntu 18.04.4 qemu-commandline 选项失败
- javascript - 与 redux 反应的受控范围滑块组件
- python - Numpy字符串数组,赋值
- reactjs - 搜索过滤器未正确呈现 FlatList
- swift - 如何从 Observer 访问循环中的每个节点?
- asp.net - Blazor 页面:如何使用 BuildRenderTree 添加动态内容
- json - 如何在 OCaml 中获取字符串片段?
- css - 如何在 react-select 组件中使用 Material UI 主题(预定义的颜色等)?
- asp.net - ASP.NET Web Api 2 控制器版本控制。找不到路线
- reactjs - 允许组件检测 Gatsby 中的路由变化