【问题标题】:Promisified function type承诺函数类型
【发布时间】:2018-10-04 12:53:19
【问题描述】:

我正在编写一个 RFC 库。 基本上我有一个在服务器上实现并由客户端中的Proxy 包装的接口。 Proxy 然后在后台进行 http 调用以调用服务器上的方法。

这适用于已经返回 Promise 的函数。 然而,在客户端函数将始终通过 Proxy 包装器返回 Promise,但类型系统不知道这一点。

因此,我使用以下代码创建了一个映射类型,将函数的返回类型更改为Promise

// Generic Function definition
type AnyFunction = (...args: any[]) => any;
// Extracts the type if wrapped by a Promise
type Unpacked<T> = T extends Promise<infer U> ? U : T;

type PromisifiedFunction<T extends AnyFunction> =
    T extends () => infer U ? () => Promise<Unpacked<U>> :
    T extends (a1: infer A1) => infer U ? (a1: A1) => Promise<Unpacked<U>> :
    T extends (a1: infer A1, a2: infer A2) => infer U ? (a1: A1, a2: A2) => Promise<Unpacked<U>> :
    T extends (a1: infer A1, a2: infer A2, a3: infer A3) => infer U ? (a1: A1, a2: A2, a3: A3) => Promise<Unpacked<U>> :
    // ...
    T extends (...args: any[]) => infer U ? (...args: any[]) => Promise<Unpacked<U>> : T;

type Promisified<T> = {
    [K in keyof T]: T[K] extends AnyFunction ? PromisifiedFunction<T[K]> : never
}

示例:

interface HelloService {
    /**
    * Greets the given name
    * @param name 
    */
    greet(name: string): string;
}

function createRemoteService<T>(): Promisified<T> { /*...*/ }

const hello = createRemoteService<HelloService>();
// typeof hello = Promisified<HelloService>
hello.greet("world").then(str => { /*...*/ }) // all fine here
// typeof hello.greet = (a1: string) => Promise<string>

一切正常,那么问题出在哪里?

我不喜欢这个实现的是,参数名称和文档会丢失(至少在 vs 代码中)。

因此,对于只想使用服务的人来说,在外部某处查找服务定义并不是一种很好的开发体验。

我不喜欢的另一件事是我必须为每个数量的参数编写一个定义。但我想在 Typescript 支持 Variadic Types 之前没有其他办法。

编辑: 在继续处理这个问题时,我发现了一个更大的问题: 接口中有重载函数,映射的接口无法正确推断。

interface HelloService {
    greet(name: string): string;
    greet(id: number): string;
}

根据函数的顺序,映射类型为
typeof hello.greet = (a1: string) =&gt; Promise&lt;string&gt;
typeof hello.greet = (a1: number) =&gt; Promise&lt;string&gt;

但它应该是:
typeof hello.greet = (a1: string|number) =&gt; Promise&lt;string&gt;

那么有什么改进的建议吗?

【问题讨论】:

  • 不,99.9% 肯定没有办法对此进行改进,您将丢失函数名称。不幸的是,没有映射函数
  • @AluanHaddad 颠倒顺序将导致额外的必需参数:typeof hello.greet = (a1: string, a2: {}, a3: {} ...) =&gt; Promise&lt;string&gt;
  • 我明白了。我读错了。还是不习惯那个位置的三元,错过了

标签: typescript visual-studio-code typescript-typings


【解决方案1】:

有了新的 Typescript 3.0,可以使用generic rest parameters

这允许我们像这样指定类型:

type UnpackPromise<T> = T extends Promise<infer U> ? U : T;

type AnyFunction<U extends any[], V> = (...args: U) => V;
type Promisified<T> = {
    [K in keyof T]: T[K] extends AnyFunction<infer U, infer V> ? (...args: U) => Promise<UnpackPromise<V>> : never;
}

【讨论】:

    猜你喜欢
    • 2020-02-16
    • 2021-06-17
    • 2016-11-11
    • 2017-09-16
    • 1970-01-01
    • 2017-06-25
    • 2021-04-18
    • 1970-01-01
    • 2017-05-16
    相关资源
    最近更新 更多