具有未知参数的通用函数参数

Phi*_*ley 2 typescript typescript-generics

将泛型参数指定为函数是否...args: unknown[]效果不佳?

// Using TS 4.2.3
const fn = (foo: boolean) => 'hello'

// Ideally, one generic function arg
type Data1<M extends (...args: unknown[]) => unknown> = {
  fn: M
}
const data1: Data1<typeof fn> = {
                   ^^^^^^^^^
  fn,
}
Run Code Online (Sandbox Code Playgroud)
Type '(foo: boolean) => string' does not satisfy the constraint '(...args: unknown[]) => unknown'.
  Types of parameters 'foo' and 'args' are incompatible.
    Type 'unknown' is not assignable to type 'boolean'.
Run Code Online (Sandbox Code Playgroud)

这个TypeScript Playground示例还演示了将函数拆分为两个通用参数,这可以工作,但从 API 的角度来看并不好。我希望更好地理解为什么一个有效,但另一个无效。

ale*_*xor 7

这是由于差异而发生的而发生的。

\n
declare function fn(foo: boolean): string\n\ntype Data1<M extends (...args: unknown[]) => unknown> = {\n  fn: M\n}\n\nconst data1: Data1<typeof fn> = {\n                      ^\n// Type \'(foo: boolean) => string\' does not satisfy the constraint \'(...args: unknown[]) => unknown\'.\n//  Types of parameters \'foo\' and \'args\' are incompatible.\n//    Type \'unknown\' is not assignable to type \'boolean\'.\n  fn,\n}\n
Run Code Online (Sandbox Code Playgroud)\n

接受函数类型的数据类型Data1在该函数的结果类型中是协变的,在函数的参数类型中是逆变的。将其 ( Data1) 想象为一个接受参数的函数(它在类型级别上的真正含义)(foo: boolean) => string。该函数必须检查该参数是否可“分配”给参数的类型(...args: unknown[]) => unknown。要检查此检查器必须确保两件事:

\n
    \n
  • 函数的返回类型string必须可分配给unknown. 这是完全有可能的。所有类型均可分配给unknown.

    \n
  • \n
  • 参数的参数类型unknown[]必须可分配给函数的参数类型 [ boolean]。这就是检查器引发错误的地方。因为unknown只能分配给unknown和any。

    \n
  • \n
\n

为什么当涉及到争论时它必须发生在相反的方向?因为作为参数传递的函数是数据的使用者。它必须处理比预期作为参数的函数更广泛的类型。简单的例子:

\n
function lowerCase(s: string): string {\n  return s.toLowerCase()\n}\n\nfunction callCb(cb: (sn: string | number) => string | number): number {\n  const stringOrNumber = cb(Math.random() ? \'string\' : 10)\n\n  return typeof stringOrNumber === \'string\' \n    ? stringOrNumber.length \n    : stringOrNumber  \xe2\x80\x8b\n} \n\ncallCb(lowerCase)\n
Run Code Online (Sandbox Code Playgroud)\n

TS 游乐场strictFunctionTypes关闭以说明不健全。

\n

虽然可以返回预期参数返回类型(字符串、数字、字符串|数字)cb的任何子集,但必须能够处理每种类型callCbcbcallCb可以提供给它的

\n
\n
type Data2<Args extends unknown [], Return extends unknown> = {\n \xe2\x80\x8bfn: (...args: Args) => Return\n}\n\nconst data2: Data2<Parameters<typeof fn>, ReturnType<typeof fn>> = {\n \xe2\x80\x8bfn,\n}\n
Run Code Online (Sandbox Code Playgroud)\n

至于你的Data2类型。您为其提供了准确的fn您在协变/正位置首先它检查提供的类型是否满足约束:

\n

\xe2\x80\x8b* 第一个类型参数[boolean]可分配给unknown[]\n\xe2\x80\x8b* 第二个类型参数string可分配给unknown

\n

然后它检查您在 type 赋值右侧传递的对象是否{ fn: (foo: boolean) => string }可分配给Data2result type { fn: (...[boolean]) => string }。显然是这样。

\n
\n

strictFunctionTypes您可以通过禁用中的选项来关闭此行为tsconfig.json。然后打字稿将函数参数视为双变量。尽管我强烈建议不要这样做。\n\xe2\x80\x8b

\n