typescript - 具有重载函数的类型推断
问题描述
考虑以下重载函数:
function foo(arg1: string, cb: (err: Error|null, res: string) => void): void
function foo(arg1: string, arg2: string, cb: (err: Error|null, res: string) => void): void
我想promisify
使用这样的功能。但默认实现返回无效类型。
当它返回时
(arg1: string, arg2: string) => Promise<{}>
我希望它会回来
{
(arg1: string): Promise<string>;
(arg1: string, arg2: string): Promise<string>;
}
考虑到这一点,我想修正打字问题。我设法使用以下方法覆盖了该特定原型:
export type Callback<T> = (err: Error | null, reply: T) => void;
export type Promisify<T> =
T extends {
(arg1: infer T1, cb?: Callback<infer U>): void;
(arg1: infer P1, arg2: infer P2, cb?: Callback<infer U2>): void;
} ? {
(arg1: T1): Promise<U>;
(arg1: P1, arg2: P2): Promise<U2>;
} :
T extends (cb?: Callback<infer U>) => void ? () => Promise<U> :
T extends (arg1: infer T1, cb?: Callback<infer P>) => void ? (arg1: T1) => Promise<P> :
T extends (arg1: infer T1, arg2: infer T2, cb?: Callback<infer U>) => void ? (arg1: T1, arg2: T2) => Promise<U> :
T extends (arg1: infer T1, arg2: infer T2, arg3: infer T3, cb?: Callback<infer U>) => void ? (arg1: T1, arg2: T2, arg3: T3) => Promise<U> :
T extends (arg1: infer T1, arg2: infer T2, arg3: infer T3, arg4: infer T4, cb?: Callback<infer U>) => void ? (arg1: T1, arg2: T2, arg3: T3, arg4: T4) => Promise<U> :
T;
但这需要我专门列出所有潜在的方法重载。
有没有办法一次转换所有方法重载,类似于我们如何转换对象属性?
解决方案
我们可以在剩余参数和展开表达式中使用元组的 3.0 特性来获得重载参数的联合,但我们需要为函数具有的每个重载数量添加一个案例:
export type GetOverloadArgs<T> =
T extends { (...o: infer U) : void, (...o: infer U2) : void, (...o: infer U3) : void } ? U | U2 | U3:
T extends { (...o: infer U) : void, (...o: infer U2) : void } ? U | U2 :
T extends { (...o: infer U) : void } ? U : never
所以例如对于 foo
type fooParams = GetOverloadArgs<typeof foo>
// will be
type fooParams = [string, (err: Error | null, res: string) => void] | [string, string, (err: Error | null, res: string) => void]
由此我们可以使用类似于您的类型Promisify
为 union 中的每个参数集创建一个函数:
export type PromisifyOne<T extends any[]> =
T extends [Callback<infer U>?] ? () => Promise<U> :
T extends [infer T1, Callback<infer P>] ? (arg1: T1) => Promise<P> :
T extends [infer T1, infer T2, Callback<infer U>?] ? (arg1: T1, arg2: T2) => Promise<U> :
T extends [infer T1, infer T2, infer T3, Callback<infer U>?]? (arg1: T1, arg2: T2, arg3: T3) => Promise<U> :
T extends [infer T1, infer T2, infer T3, infer T4, Callback<infer U>?] ? (arg1: T1, arg2: T2, arg3: T3, arg4: T4) => Promise<U> :
T;
并且使用条件类型的分配行为,我们可以创建所有重载的联合:
export type Promisify<T> =PromisifyOne<GetOverloadArgs<T>>
export type Promisify<T> =PromisifyOne<GetOverloadArgs<T>>
type fooOverloadUnion = Promisify<typeof foo>
// Same as
type fooOverloadUnion = ((arg1: string) => Promise<string>) | ((arg1: string, arg2: string) => Promise<string>)
为了使它再次可调用,我们可以使用将联合转换为交集,使用UnionToIntersection
,最终结果为:
export type Callback<T> = (err: Error | null, reply: T) => void;
export type PromisifyOne<T extends any[]> =
T extends [Callback<infer U>?] ? () => Promise<U> :
T extends [infer T1, Callback<infer P>?] ? (arg1: T1) => Promise<P> :
T extends [infer T1, infer T2, Callback<infer U>?] ? (arg1: T1, arg2: T2) => Promise<U> :
T extends [infer T1, infer T2, infer T3, Callback<infer U>?]? (arg1: T1, arg2: T2, arg3: T3) => Promise<U> :
T extends [infer T1, infer T2, infer T3, infer T4, Callback<infer U>?] ? (arg1: T1, arg2: T2, arg3: T3, arg4: T4) => Promise<U> :
T;
export type GetOverloadArgs<T> =
T extends { (...o: infer U) : void, (...o: infer U2) : void, (...o: infer U3) : void } ? U | U2 | U3:
T extends { (...o: infer U) : void, (...o: infer U2) : void } ? U | U2 :
T extends { (...o: infer U) : void } ? U : never
type UnionToIntersection<U> = (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never
export type Promisify<T> = UnionToIntersection<PromisifyOne<GetOverloadArgs<T>>>
// Sample
declare function foo(arg1: string, cb: (err: Error|null, res: string) => void): void
declare function foo(arg1: string, arg2: string, cb: (err: Error|null, res: string) => void): void
declare const fooPromise: Promisify<typeof foo>
let r = fooPromise("")
let r2 = fooPromise("", "")
推荐阅读
- kubernetes - GKE 上的 Kubernetes 持久卷未安装
- python - 调用外部 Python 代码时强制打开 Python 控制台
- angular - 分页后如何动态获取Angular FormArray字段中的绝对索引?
- c# - 具有相同元素的 XML 文件
- google-authenticator - 如何在我的应用程序(nodeJS)中集成谷歌身份验证器?
- android - 无法解决:com.android.support:appcompat-v7:28.3.0
- rate - 对于 prometheus 和 datadog,指标的每秒速率值会有所不同
- c# - System.Net.WebUtility.UrlEncode 和 System.Web.HttpUtility.UrlEncode 方法的区别
- angular - Angular将服务注入基类但不在子类中,并在子类中使用父服务
- c# - 在 Xamarin Forms Shell 中隐藏 TabBar