为什么不在 ts 严格模式下Parameters<Func>扩展unknown[] (Array<unknown>)

Geo*_*ids 1 extends types arguments typescript spread-syntax

标题几乎说明了一切。我有这个代码:

  type testNoArgsF = () => number;
  type testArgsF = (arg1: boolean, arg2: string) => number;
  type unknownArgsF = (...args: unknown[]) => number;
  type anyArgsF = (...args: any[]) => number;

  type testII = testArgsF extends anyArgsF ? true : false; // true
  type testIII = Parameters<testArgsF> extends Parameters<unknownArgsF>
    ? true
    : false; // true

  // unexpected:
  type testIV = testArgsF extends unknownArgsF ? true : false; // false <- why?
  // even though:
  type testV = testNoArgsF extends unknownArgsF ? true : false; // true
Run Code Online (Sandbox Code Playgroud)

它是用打字稿(版本 3.8)编写的,并且我启用了严格模式。意想不到的结果是,测试函数不会扩展扩展参数为 的函数类型unknown[],但如果您只检查参数,它们就会扩展unknown[]。由于返回类型始终是数字,我不明白还有什么可能不同来伪造该extends语句。

其他注意事项:

  • 仅当您的测试函数有 0 个参数时,extend 语句才为 true。
  • 如果关闭严格模式,则不会出现此行为。

jca*_*alz 6

--strictFunctionTypes启用编译器选项后,将对函数类型参数进行逆变检查。“逆变”是指函数的子类型关系与函数参数的子类型关系变化方向相反。因此A extends B,如果 ,则(x: B)=>void extends (x: A)=>void 反之亦然

由于 TypeScript 中“可替代性”的本质(也称为行为子类型) ,这是一个类型安全问题。如果A extends B是 true,您应该能够将 an 用作Aa B。如果你不能,那就A extends B不是真的。

如果关闭--strict,则编译器将使用 TS-2.6 之前的双变量检查函数参数的行为,这是不安全的,但出于生产力的原因是允许的。这可能在这里偏离主题,但您可以在 TypeScript 常见问题解答条目“为什么函数参数是双变的?”中阅读更多相关信息。


无论如何,如果您需要一个接受任何数字unknown参数的函数类型,则不能安全地使用仅 的特定子类型的函数unknown。观察:

const t: testArgsF = (b, s) => (b ? s.trim() : s).length
const u: unknownArgsF = t; // error!

u(1, 2, 3); // explosion at runtime! s.trim is not a function
Run Code Online (Sandbox Code Playgroud)

如果testArgsF extends unknownArgsF为真,那么您将能够毫无错误地分配t给上面的内容,当愉快地接受非第二个参数时,立即导致运行时错误。 uustring

您可以看到,子类型/实现函数类型的唯一安全方法是子类型/实现接受与超类型/调用签名所期望的参数相同或更宽的参数。这就是--strictFunctionTypes引入该语言的原因。


如果您更改unknownany(使用anyArgsF而不是unknownArgsF),那么编译器不会抱怨,因为在 TypeScript 中故意any健全。该类型any被认为可以分配给所有其他类型,也可以其他类型分配;这是不安全的,因为例如string extends anyany extends number都是 true 而string extends number是 false。any因此,当涉及到上述替代原则时,不强制执行。将值注释为any类型相当于放松或关闭对该值的类型检查。这并不能避免运行时错误;它只是消除编译器的错误:

const a: anyArgsF = t; // okay, type checking with any is disabled/loosened
a(1, 2, 3); // same explosion at runtime!
Run Code Online (Sandbox Code Playgroud)

在 为 true 的情况下testNoArgsF extends unknownArgsF,这也是可替代性的结果。您可以使用不带参数的函数,就好像它是任何函数类型一样,因为它(通常)最终会忽略传递给它的任何参数:

const n: testNoArgsF = () => 1;
const u2: unknownArgsF = n; // okay
u2(1, 2, 3); // okay at runtime, since `n` ignores its arguments
Run Code Online (Sandbox Code Playgroud)

TypeScript FAQ 条目“为什么参数较少的函数可以分配给参数较多的函数?”对此进行了解释。


好的,希望有帮助;祝你好运!

Playground 代码链接