Typescript:如何输入通用高阶函数

问题描述 投票:0回答:1

我有以下实用功能:

async function resolvePromises<F extends (arg: any) => any>(
  data: Parameters<F>,
  list: F[] | F[][],
): Promise<boolean> {
  for (const item of list) {
    if (!Array.isArray(item)) {
      await item(data);

      continue;
    }

    await Promise.allSettled(item.map((action) => action(data)));
  }

  return true;
}

它的实例化如下:

const wait = ({ time = 1000 }) => new Promise((resolve) => setTimeout(resolve, time));

resolvePromises({ time: 2000 }, [wait, wait]);

我希望 TypeScript 只允许“数据”参数与提供的基于 Promise 的函数相匹配,因此使用上面的示例,这会出错:

resolvePromises({ time: 2000, name: 'John' }, [wait, wait]);

已经快到了,但是

resolvePromises
的第一个参数总是显示以下内容:

如果我将

resolvePromises
的第一个参数更改为数组,例如:

resolvePromises([{ time: 2000 }], [wait, wait]);

当我向函数不支持的第一个参数添加属性时,我正确地得到了错误。不过

wait
函数的第一个参数不是数组..

有人可以告诉我我输入的

resolvePromises
有什么问题吗?为什么?

编辑 该函数也可以使用嵌套的 Promise 数组来调用,例如:

resolvePromises({ time: 1000}, [[wait, wait],[wait]]);

非常感谢!

javascript typescript types promise
1个回答
0
投票

Parameters<T>
实用程序类型将解析为类似数组的元组类型。如果
data
Parameters<F>
类型,那么您必须向那里传递一个数组。

相反,如果您希望

data
成为 first 参数(同时仍使用
Parameters<T>
),则可以使用 0 对其进行
索引:

async function resolvePromises<F extends (arg: any) => any>( data: Parameters<F>[0], // <-- here list: F[] | F[][], ): Promise<boolean> { for (const item of list) { if (!Array.isArray(item)) { await item(data); continue; } await Promise.allSettled(item.map((action) => action(data))); } return true; }
现在事情按照你期望的方式进行:

const wait = ({ time = 1000 }) => new Promise((resolve) => setTimeout(resolve, time)); resolvePromises({ time: 2000 }, [wait, wait]); // okay resolvePromises({ time: 2000, name: 'John' }, [wait, wait]); // error!
请注意,您想要获得的特定“多余属性警告”并不是真正的类型安全问题,因此很脆弱。多余的属性不会违反类型(例如,

{time: 2000, name: 'John'}是完全有效的{time: number}

); 
结构类型
允许额外的属性。多余的属性检查更多的是一种 linter 功能,而不是类型安全功能,您不能真正依赖它们随处发生。
表示函数的传统方法是在参数类型 A

中使其成为

generic 并返回类型 R

 而不是函数类型 
F
,但这并不关心多余的属性:
async function resolvePromises<A, R>(
  data: A,
  list: ((a: A) => R)[] | ((a: A) => R)[][],
): Promise<boolean> {
  for (const item of list) {
    if (!Array.isArray(item)) {
      await item(data);
      continue;
    }
    await Promise.allSettled(item.map((action) => action(data)));
  }
  return true;
}

resolvePromises({ time: 2000 }, [wait, wait]); // okay
resolvePromises({ time: 2000, name: 'John' }, [wait, wait]); // okay

如果您仍然关心多余的属性,您可以指导推理,因此
A
仅从

list

 推断,而不是使用 
ms/TS#14829
 中描述的技术从 
data
 推断:
async function resolvePromises<A, R>( data: A & {}, list: ((a: A) => R)[] | ((a: A) => R)[][], ): Promise<boolean> { for (const item of list) { if (!Array.isArray(item)) { await item(data); continue; } await Promise.allSettled(item.map((action) => action(data))); } return true; } resolvePromises({ time: 2000 }, [wait, wait]); // okay resolvePromises({ time: 2000, name: 'John' }, [wait, wait]); // error

Playground 代码链接

© www.soinside.com 2019 - 2024. All rights reserved.