【问题标题】:use typescript generics to infer type of param function使用打字稿泛型来推断参数函数的类型
【发布时间】:2020-03-01 14:00:40
【问题描述】:
function validate<K>(validationFunc: (...args: (K extends Array<infer T> ? T : K)[]) => boolean, validationArgs: K[]): boolean {
  let res: boolean;
  for (const validationArg of validationArgs) {
    if (Array.isArray(validationArg)) {
      res = validationFunc(...validationArg);
    } else {
      // res = validationFunc(validationArg);
      res = (validationFunc as (args: K) => boolean)(validationArg);
    }
    if(!res) 
      return false;
  }
  return true
}

注释行在参数处引发错误:the Argument of type 'K' is not assignable to parameter of type 'K extends (infer T)[] ? T : K'.ts(2345),而强制转换的版本可以正常工作,并且不会引发任何错误。 如this playground 所示。

为什么打字稿不能推断,在这一行,K 不能是 Array&lt;any&gt; 类型,因此允许传递给验证函数?

语义上: 如果第二个参数的类型为K[],则函数需要接受K 作为单个参数。 如果第二个参数是K[][]类型,函数需要接受K的多个参数。

例如

validate((x: number) =&gt; x%2, [1, 2, 3])应该没问题

validate((a: string, b: string) =&gt; a === b, [['a', 'a'], ['b', 'b']])应该没问题

validate((x: number) =&gt; x%2, ['a', 'b']) 应该会抛出错误

validate((x: number) =&gt; x%2, [['a', 'a'], ['b', 'b']]) 应该会抛出错误

编辑: validate((x: number) =&gt; x % 2 === 0, [[1, 2, 3]]) 也应该抛出错误,因为 validate 会破坏 number[][] 一次,并尝试使用 number[] 调用 (x: number) =&gt; boolean

【问题讨论】:

  • tested it on TS playground 我得到了你所期望的。您使用 TS 的哪个版本/编译器选项?
  • 我用的是最新版3.6.4。这些例子只是为了说明,我想在语义上实现什么。问题不在于方法声明(在我的代码和操场上运行良好)。问题出在身体上。我认为我不应该被要求投射validationFunc。该问题在操场上变得可见(第 10 行)(请参阅有问题的编辑)

标签: typescript type-inference typescript-generics


【解决方案1】:

我认为您不需要推断 param 函数的类型。正如您所解释的那样,您的验证参数可以只是 K[] | K[][]

当你调用验证函数x % 2 时我也做了一个小改动,应该包裹在Boolean() 中,否则返回值会不正确。

function validate<T extends (...args: any) => boolean, P extends Parameters<T>>(validationFunc: T, validationArgs: P[]): boolean {
  let res: boolean;

  for (const validationArg of validationArgs) {
    res = validationFunc(validationArg);
    if(!res) 
      return false;
  }
  return true
}

function simplified<T extends (...args: any) => boolean, P extends Parameters<T>>(validationFunc: T, validationArgs: P[]): boolean {
  return validationArgs.every((args) => validationFunc(args));
}

validate((x: number) => Boolean(x % 2), [[1], [2], [3]]) // should be ok

validate((a: string, b: string) => a === b, [['a', 'a'], ['b', 'b']]) // should be ok

validate((x: number) => Boolean(x % 2), [['a'], ['b']]) // should throw an error

validate((x: number) => Boolean(x % 2), [['a', 'a'], ['b', 'b']]) // should throw an error

validate((x: number) => x % 2 === 0, [[1, 2, 3]]); // should throw an error

Playground Link

【讨论】:

  • 好的,我添加了另一个示例,如果我不推断内部类型,它不会被错误捕获。抱歉,我之前错过了。如果不是这种情况,您的解决方案可以正常工作,但可能会在运行时导致错误。 (我的也没有)
  • 已编辑,但需要进行一些更改,我认为您想要实现的目标是不可能的,如果有人找到更好的答案,我自己很好奇.. :) 我认为这是当您提供 [1, 2, 3] 作为参数时会模棱两可,因为它可以用于一次执行具有 3 个参数的函数或执行 3 次具有 1 个参数的函数。
  • 还添加了一个简化版本,您可能只需使用Array.every() 即可。
  • @Grabofus,在我看来不是。我总是提供一个数组。该数组中的每一项都将用于一次迭代。如果这个外部数组中的项目本身就是数组,它们应该被传播并作为单独的参数传递给validationFunc。 [1, 2, 3] -> 三次迭代,每次 1 个数字 validationFunc(x)[[1, 2, 3]] => 只打一个电话给vaildationFunc(1,2,3)
  • @RobertKoritnik every 与否定的some 相同,它将在第一个false 处停止
猜你喜欢
  • 2020-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-15
  • 1970-01-01
  • 2022-11-23
  • 2018-05-02
相关资源
最近更新 更多