为什么在键入接受函数的高阶函数参数时键入“Function”不起作用?

Why type `Function` doesn't work when typing a higher-order function parameter that accepts a function?

我想输入一个函数作为输入:

  1. 数组
  2. 过滤逻辑函数(谓词函数)

在 JavaScript 中,以下按预期工作:

const myFilter = (func, arr) => arr.filter(func)
const myArr = [1, 2, 3, 4, 5, 6]
const res = myFilter(x => x > 3, myArr)

console.log(res) // => [ 4, 5, 6 ] 

但是,在 TypeScript 中它会抛出一个错误:

const myFilter = (func: Function, arr: any[]): any[] => arr.filter(func)
//                                                                  ^
//                                                        (parameter) func: Function
//                                                        No overload matches this call.

虽然输入funcany解决了错误,但我还是想知道:

  1. 为什么那个类型 Function 不起作用?
  2. func 参数的正确类型是什么?

Function指的是js函数class用于从字符串创建函数

改为使用以下语法:func: (current: T, i: number, arr: T[]) => boolean。此类型创建一个 Array.prototype.filter 接受的回调类型,因为它模仿了它的参数和 return 类型。

使用泛型,您可以拥有接受任何类型的 HOF 函数:

function myFilter<T>(func: (current: T, i: number, arr: T[]) => boolean, arr: T[]): T[] {
  return arr.filter(func);
}

console.log(myFilter((v) => v % 2 === 0, [1, 2, 3, 4, 5, 6]));

如果您不想使用泛型并将函数限制为特定类型,只需删除 <T> 并将 T 替换为所需的类型。

有了泛型就可以写了,看第二个参数func定义的好像是回调。 Array<T> - 这被称为泛型类型,其中 T 是调用函数时提供的泛型类型,无论你提供什么都会变成 T。Sp 它可以是数字或字符串或整数,如果你传递你的数字例如发送 T 将 te 号码。

const myFilter = <T,>(arr: Array<T>, func: (value: T) => any) => {
   return arr.filter(func);
}

const myArr = [1, 2, 3, 4, 5, 6];
const res = myFilter(myArr, x => x > 3);

console.log(res);

你甚至可以像这样限制类型。所以现在你必须总是传递数字数组而不是其他任何东西。

const myFilter = <T extends number>(arr: Array<T>, func: (value: T) => any) => {
   return arr.filter(func)
}

根据你的具体问题,不使用泛型,可以简单地写成这样,

const myFilter = (arr: Array<any>, func: (value: number) => any) => {
   return arr.filter(func)
}

const myArr = [1, 2, 3, 4, 5, 6]
const res = myFilter(myArr, x => x > 3)

console.log(res);

为了最大化你的函数的效用,你可以这样写multiple overloads:

通过为类型保护版本编写一个重载,您可以推断谓词中的保护类型并在原始 array/tuple 是齐次的情况下得到一个缩小的数组(参见示例 2)。

TS Playground

type ArrayItemPredicate<U, T extends U> = (
  value: U,
  index: number,
  array: readonly U[],
) => value is T;

function myFilter <U, T extends U, A extends readonly U[]>(
  arr: A,
  filterPredicate: ArrayItemPredicate<U, T>,
): T[];
function myFilter <A extends readonly unknown[]>(
  arr: A,
  filterPredicate: Parameters<A['filter']>[0],
): A[number][];
function myFilter <
  U,
  T extends U,
  A extends readonly U[],
  Predicate extends ArrayItemPredicate<U, T> | Parameters<A['filter']>[0],
>(arr: A, filterPredicate: Predicate) {
  return arr.filter(filterPredicate);
}


{ // example 1: from your question
  const arr = [1, 2, 3, 4, 5, 6];
  const predicate = (n: number) => n > 3;

  const res = myFilter(arr, predicate); // number[]
  console.log(res); // [4, 5, 6]
}

{ // example 2: filter to keep only numbers
  const arr = [1, 'a', 2, 'b', 3, 4, 'c', 'd', 'e', 'f', 5, 6];
  const predicate = (value: string | number): value is number => typeof value === 'number';

  const res = myFilter(arr, predicate); // number[]
  console.log(res); // [1, 2, 3, 4, 5, 6]
}