承诺函数类型
Promisified function type
我正在编写一个 RFC 库。
基本上我有一个在服务器上实现并由客户端中的 Proxy
包装的接口。 Proxy
然后在后台进行 http 调用以调用服务器上的方法。
这适用于已经 return 和 Promise
的函数。
然而,在客户端函数中,总是会通过 Proxy
包装器 return 一个 Promise
,但类型系统不知道这一点。
因此,我使用以下代码创建了一个映射类型,以将函数的 return 类型更改为 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) => Promise<string>
或
typeof hello.greet = (a1: number) => Promise<string>
但它应该是:
typeof hello.greet = (a1: string|number) => Promise<string>
关于如何改进这个问题有什么建议吗?
使用新的 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;
}
我正在编写一个 RFC 库。
基本上我有一个在服务器上实现并由客户端中的 Proxy
包装的接口。 Proxy
然后在后台进行 http 调用以调用服务器上的方法。
这适用于已经 return 和 Promise
的函数。
然而,在客户端函数中,总是会通过 Proxy
包装器 return 一个 Promise
,但类型系统不知道这一点。
因此,我使用以下代码创建了一个映射类型,以将函数的 return 类型更改为 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) => Promise<string>
或
typeof hello.greet = (a1: number) => Promise<string>
但它应该是:
typeof hello.greet = (a1: string|number) => Promise<string>
关于如何改进这个问题有什么建议吗?
使用新的 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;
}