Typescript 泛型中的错误,或者只是我愚蠢? [英] Bug in Typescript generics, or just me being stupid?

查看:36
本文介绍了Typescript 泛型中的错误,或者只是我愚蠢?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在进行大量的 TypeScript 练习(typescript-exercises.github.io,练习 14),而且我似乎陷入了在我看来像是打字稿错误的问题.但由于我对 TypeScript 没有那种经验,所以我首先会在这里询问它是否真的是一个错误,或者只是我愚蠢.在本练习中,您需要将类型添加到一堆函数中,除了任意数量的参数,如果参数数量不足,则返回子函数.这里的问题是 TypeScript 似乎没有正确使用泛型.如果我检查 IDE 最后一行中使用的 func 的类型,它会被读取为 func<*, number>,其中 * 是 complete强> 类型的添加,而不仅仅是数字.是我做错了什么还是这真的是 TypeScript 的行为?

I'm currently in the middle of an extensive TypeScript exercise (typescript-exercises.github.io, exercise 14), and I seem to be getting stuck on what looks to me like a typescript bug. But since I'm not that experience with TypeScript I'll first ask here to see if it really is a bug, or just me being stupid. In this exercise you need to add types to a bunch of functions that except any amount of arguments, and return subfunctions if the amount of arguments is insufficient. Problem here is that TypeScript doesn't seem to get the generics right. If I check the type of func used in the last line in my IDE, it's read as func<*, number>, with * being the complete type of add instead of just number. Am I doing something wrong or is this really TypeScript acting up?

declare function func<T1, T2>(subFunc: (arg1: T1, arg2: T2) => T1);
function add(a: number, b: number): number;
function add(a: number): (b: number) => number;
function add(): typeof add;
function add(a?: number, b?: number) {
  if (a === undefined)
    return add;
  if (b === undefined) {
    return function subAdd(b?: number) {
      if (b === undefined)
        return subAdd;
      return a + b;
    }
  }
  return a + b;
}

console.log( func(add) );

实际练习禁止更改测试(最后一行),因此不幸的是,手动提供泛型不是一种选择.

The actual exercise prohibits changing the test (last line), so manually supplying the generics is unfortunately not an option.

附言这是对实际练习的简化.如果你想看看原点,这是在 test.ts 中的一个测试中用作减速器的 add 函数.

P.S. This is a simplification of the actual exercise. If you want to look at the origin, this is the add function being used as the reducer in one of the tests in test.ts.

推荐答案

这不完全是一个错误,而是由于 TypeScript 的设计限制.

It's not exactly a bug, but it is due to a design limitation of TypeScript.

您遇到的问题与 microsoft/TypeScript#27027;当编译器尝试从重载的函数类型(具有多个调用签名的函数)进行推断时,它只检查最后一个调用签名.这不是您想要的,这是编译器根据将/将被调用的方式选择重载签名;不幸的是,编译器无法做到这一点;因此存在设计限制.

The issue you're hitting is the same as in microsoft/TypeScript#27027; when the compiler tries to infer from an overloaded function type (a function with multiple call signatures), it only examines the last call signature. This is instead of what you want, which is for the compiler to choose an overload signature based on how it will/would be called; which the compiler unfortunately cannot do; hence the design limitation.

所以在func(add)中,不会发生的是:编译器看到add需要匹配类型(arg1: T1, arg2: T2) =>T1,所以选择调用签名(a: number, b: number) =>number 因为这是第一个匹配的.因此T1被推断为number,而T2也被推断为number.

So in func(add), what does not happen is: the compiler sees that add needs to match type (arg1: T1, arg2: T2) => T1, so it selects the call signature (a: number, b: number) => number because that's the first one that matches. Therefore T1 gets inferred as number, and T2 also gets inferred as number.

所做的是:编译器看到add需要匹配类型(arg1: T1, arg2: T2) =>T1.因为add 是一个重载函数,它忽略除了最后一个调用签名之外的所有内容,即() =>添加类型.现在这 确实 匹配 (arg: T1, arg2: T2) =>T1,因为 更少参数的函数可以分配给更多参数的函数(TL;DR 通常,函数忽略调用它们的额外参数是安全的,并且有很多有时这是可取的).因此,对于任何一个参数都没有有用的推理站点,但是对于返回类型 T1 有一个很好的推理站点.所以编译器推断 T1typeof add,而 T2unknown 因为没有什么比它更好的了推断(类型参数被隐式约束为未知,当泛型类型推断失败,编译器放弃并扩展到约束).

What does happen is: the compiler sees that add needs to match type (arg1: T1, arg2: T2) => T1. Because add is an overloaded function, it ignores everything except the last call signature, which is () => typeof add. Now this does match (arg: T1, arg2: T2) => T1, because functions of fewer parameters are assignable to functions of more parameters (TL;DR it is generally safe for functions to ignore extra parameters they are called with, and there are lots of times when this is desirable). There is therefore no useful inference site for either argument, but there is a good inference site for the return type, T1. And so the compiler infers that T1 is typeof add, and T2 is unknown because there's nothing better for it to infer (type parameters are implicitly constrained to unknown, and when generic type inference fails the compiler gives up and widens to the constraint).

所以你看到 func(unknown,不是 number,对吧?),你很难过.

And so you see func<typeof add, unknown> (unknown, not number, right?) and you're sad.

我不确定应该对您的代码做些什么来处理这个问题.我不清楚为什么 func() 需要对 add 进行操作,一般来说,尝试在高阶位置使用重载函数往往会遇到您的限制看这里.重载函数实际上是为了直接调用,仅此而已.

I'm not sure what, if anything, should be done to your code to deal with this. It's not clear to me why func() needs to operate on add at all, and generally speaking trying to use overloaded functions in higher order positions tends to run into the limitation you see here. Overloaded functions are really meant to be called directly and that's it.

对于这个特定的问题,您可以通过颠倒重载的顺序来处理它,例如

For this particular issue you could deal with it by reversing the order of your overloads, like

declare function add(): typeof add;
declare function add(a: number): (b: number) => number;
declare function add(a: number, b: number): number;

这将导致 T1T2 根据需要被推断为 number:

which will cause T1 and T2 to be inferred as number as desired:

console.log(func(add));
// function func<number, number>(subFunc: (arg1: number, arg2: number) => number): void

但是一旦您尝试一些稍微不同的 func 之类的东西,需要访问不同的调用签名,您就会再次遇到同样的问题.

but as soon as you try some slightly different func-like thing that needs to access a different call signature, you'll have the same problem again.

游乐场链接代码

这篇关于Typescript 泛型中的错误,或者只是我愚蠢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆