TypeScript:在 Promise<T> 中包装函数参数

TypeScript: Wrap function parameters in Promise<T>

我尝试创建一个采用函数类型的类型,将函数参数包装在 Promise<> 和 returns 一个新类型中 - 相同的函数但参数与 Promise 相同,例如:

type PromisedFn = PromisedArgs<(a: number, b: string | number) => void>
// should produce (a: Promise<number>, b: Promise<string | number>) => void

我绞尽脑汁想了几个小时才得到这样的代码:

type Func = (id: number, guid: number | string) => number | string; 
type FuncParams = Parameters<Func>;
// FuncParams = [id: string, guid: string | number]

type FuncWrappedParams = {
  [K in keyof FuncParams as Extract<K, '0'|'1'>]: Promise<FuncParams[K]>
}
// { 0: Promise<number>, 1: Promise<string | number> }

仍然无法将数字索引对象正确应用为数组:

type FuncWrappedParamsArray = [...args: FuncWrappedParams[]];
type WrappedParamsFunc = (...args: FuncWrappedParamsArray) => ReturnType<Func>;

let func: WrappedParamsFunc = (
  id: Promise<number>,
  guid: Promise<number | string>
) => 'TestString';

// ERROR:
// Type '(id: Promise<number>, guid: Promise<number | string>) => string'
// is not assignable to type 'WrappedParamsFunc'.
// Types of parameters 'id' and 'args' are incompatible.
// Type 'FuncWrappedParams' is missing the following properties from type 'Promise<number>':
// then, catch, [Symbol.toStringTag]

我不知道怎么处理这个。

问题 #1 如上。

问题#2:缺点是我们必须提前知道一些函数参数:[K in keyof FuncParams as Extract<K, '0'|'1'>].

你可以这样写PromisedArgs

type PromisedArgs<T extends (...args: any) => any> =
  T extends (...args: infer A) => infer R ? (
    (...args: { [I in keyof A]: Promise<A[I]> }) => R
  ) : never;

只要你的数组类型是generic type parameter (like A above), a mapped type over it will produce another array type。在 {[I in keyof A]: ...} 中,键类型 I 本质上只是自动迭代 numeric-like 索引。您不必手动获取 "0" | "1" | "2" | ... 或担心如何将生成的映射类型提升回数组。

使用the infer keyword in the conditional type check is just a way to get both the parameters and return type of the function at once, instead of using the Parameters<T> and the ReturnType utility types separately. If you look at the definitions of Parameters and of ReturnType你会发现它们的实现方式是一样的

无论如何,让我们确保它按预期工作:

type Func = (id: number, guid: number | string) => number | string;
type WrappedParamsFunc = PromisedArgs<Func>
/* type WrappedParamsFunc =
   (id: Promise<number>, guid: Promise<string | number>) => string | number */

let func: WrappedParamsFunc = (
  id: Promise<number>,
  guid: Promise<number | string>
) => 'TestString';

看起来不错!


顺便说一句,在 microsoft/TypeScript#27995 上报告了 TypeScript 中的一个错误,这可能会阻止您自己找到此解决方案。事实证明,您正在映射类似数组 通用类型参数 的键是很重要的。如果您尝试映射特定类型,映射将退回到遍历所有键,甚至是数组方法和东西:

type Foo = [1, 2, 3]
type Bar = { [I in keyof Foo]: Promise<Foo[I]> }; 
/* type Bar = {
    [x: number]: Promise<3 | 1 | 2>;
    0: Promise<1>;
    1: Promise<2>;
    2: Promise<3>;
    length: Promise<3>;
    toString: Promise<() => string>; 
    ...
*/

因为 Foo 是一个特定的类型而不是类型参数,所以你得到上面的混乱。您的 FuncParams 是一个特定的类型,例如 Foo,并且您尝试手动清理混乱,但效果不佳。好吧!

Playground link to code