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

use typescript generics to infer type of param function

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.

中所示

为什么 typescript 无法推断,在这一行中,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[][] 一次,并尝试用 number[]

调用 (x: number) => boolean

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

我在调用验证函数时也做了一个小改动 x % 2 应该包装在 Boolean() 中,否则 return 值将不正确。

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