我try 创建一个类型,它接受一个函数类型,将函数参数包装在Promise<>中,并返回一个新类型——同一个函数,但参数为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类型的参数(如上面的A),就可以使用mapped type over it will produce another array type.在{[I in keyof A]: ...}中,键类型I基本上只是自动地在类似数字的索引上迭代.您不必手动获取"0" | "1" | "2" | ...,也不必担心如何将生成的映射类型提升回array.

使用the infer keyword in the conditional type check只是一种同时获取函数的参数和返回类型的方法,而不是单独使用the Parameters<T>the ReturnType utility types.如果你看一下of Parametersof 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';

看起来不错!


顺便说一句,TypeScript中有一个bug,报告为microsoft/TypeScript#27995,这可能会阻止您自己找到这个解决方案.事实证明,映射类似于arraylike generic type parameter的键非常重要.如果试图映射到特定类型,映射将退回到迭代所有键,甚至是数组方法和其他内容:

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

Javascript相关问答推荐

为什么剧作家在导航时会超时?

如何用变量productId替换 1"

Node.JS ws包中是否有Webocket的错误代码列表?

鼠标移动时更新画布

在卡信息之间切换

如何提取Cypress中文本

序列查找器功能应用默认值而不是读取响应

禁用从vue.js 2中的循环创建的表的最后td的按钮

过滤对象数组并动态将属性放入新数组

硬币兑换运行超时

将旋转的矩形zoom 到覆盖它所需的最小尺寸&S容器

如何在不影响隐式类型的情况下将类型分配给对象?

元素字符串长度html

变量在导入到Vite中的另一个js文件时成为常量.

获取';无法解决导入和导入";slick-carousel/slick/slick-theme.css";';错误

在Odoo中如何以编程方式在POS中添加产品

将多个文本框中的输出合并到一个文本框中

通过跳过某些元素的对象进行映射

Jexl to LowerCase()和Replace()

在JavaScript中将Base64转换为JSON