【问题标题】:Infer type from sibling从兄弟姐妹推断类型
【发布时间】:2018-03-01 20:30:26
【问题描述】:

我正在写一些中间件,界面如下

interface Config {
    callApi<P> (api: ApiClient): Promise<P>,
    transformResponse?(payload: P): any
}

从上面可以看出,配置采用callApi 函数,该函数将调用远程API 并返回基于promise 的结果,然后可选的transformResponse 将接收有效负载并应用转换给它。关键部分是transformResponse 函数应该根据callApi 调用的返回类型推断负载的类型

问题是上面是无效的,因为P里面的transformResponse类型不能达到callApi函数的同一个变量。

有没有什么办法可以完成上面的,而不必像这样传入API响应类型

interface Config<ApiResponse> {
    callApi (api: ApiClient): Promise<ApiResponse>,
    transformResponse?(payload: ApiResponse): any
}

示例用法

const callCreateComment: Config = {
  callApi (api) => api.createComment(),
  transformResponse(payload) => ({ ...payload // infer the return type of the `api.createComment()` call })
}

【问题讨论】:

  • 这感觉就像你在要求existential types,但我不确定。您能否说明您将如何使用它以及您希望如何阻止使用它?显然,您可以只返回 Promise&lt;any&gt; 并使用 payload: any,但我猜您正在尝试执行某些操作。你真的是说Config&lt;Result&gt;callApi() 函数允许调用者 指定P?还是callApi() 会返回一些未知的调用者P 必须由transformResponse 在同一个对象上接受?
  • 我删除了 Result 类型以清理示例。重点是我希望能够在transformResponse 函数中推断/自动完成 API 调用的返回类型,而无需手动指定其返回类型

标签: typescript


【解决方案1】:

好的,我想我明白你想要什么了,就是写出callApi() 方法,然后让TypeScript 使用该方法的返回类型来约束transformResponse() 方法的参数。我不知道如何让推理与对象文字一起工作。看起来通用 ApiResponse 参数尚未及时解析,无法根据上下文将参数类型限制为 transformResponse()

我可以让推理起作用的方法是将对象文字分成两部分,并让一个函数将这些部分作为参数并将它们放在一起。这让我们可以利用left-to-right inference 来获取上下文类型的函数参数。向您展示可能更有意义:

我将为您假设这些定义,因为您没有指定它们:

interface Comment {
    something: string; // who knows
}
interface ApiClient {
    createComment(): Promise<Comment>;
}

我会给Config的方法提供一些类型别名,以便我可以重用它们:

type CallApi<R> = (api: ApiClient) => Promise<R>;
type TransformResponse<R> = (payload: R) => any;

最后,这是您的Config&lt;R&gt;,实际上与您的(编辑)版本相同,使用R 而不是ApiResponse

interface Config<R> {
    callApi: CallApi<R>,
    transformResponse?: TransformResponse<R>
}

下面是使推理起作用的函数:

const buildConfig = <R>(
    callApi: CallApi<R>, 
    transformResponse?: TransformResponse<R>
): Config<R> => ({ callApi, transformResponse });

buildConfig() 函数有两个参数。第一个是CallApi&lt;R&gt;,第二个是TransformResponse&lt;R&gt;。函数参数的从左到右的上下文类型推断意味着它将从第一个参数解析R,然后在第二个参数的上下文类型中使用它。像这样:

const callCreateComment = buildConfig(
    (api) => api.createComment(),  // R is inferred as Comment
    (payload) => ({ ...payload })  // payload is now constrained
);

您会注意到,当您输入时,payload 的上下文类型将被推断为Comment。并且callCreateComment 被键入为Config&lt;Comment&gt;,您不必根据需要在该函数的任何位置指定Comment

Playground link

希望对您有所帮助。祝你好运!

【讨论】:

  • 这真是太棒了,正是我想要的!非常感激。唯一缺少的是,有效负载被(正确地)推断为 Promise,我希望它只返回未包装的值 ,而不是承诺。这可能吗?
  • 嗯,payload 肯定被推断为 RcallApi() 的返回值为Promise&lt;R&gt;,但这是您指定的类型定义。如果您打算让它只返回R,您可以更改类型签名。如果您要问如何将Promise&lt;R&gt; 转换为R,那么separate question 已被多次询问,简短的回答是“你不能”。更长的答案是“您将需要使用异步技术”。
  • 你说得对,我现在可以正常工作了。非常感谢!
猜你喜欢
  • 2021-02-25
  • 2019-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-12
  • 2018-10-26
  • 1970-01-01
  • 2019-08-29
相关资源
最近更新 更多