使用打字稿泛型来推断参数函数的类型

时间:2019-11-05 08:27:11

标签: typescript type-inference typescript-generics

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<any>类型,因此可以传递给验证函数?

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

例如

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

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

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

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

编辑: validate((x: number) => x % 2 === 0, [[1, 2, 3]])也应该引发错误,因为验证会破坏number[][]一次,并尝试用(x: number) => boolean调用number[]

1 个答案:

答案 0 :(得分: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