简体   繁体   English

如何在不使用强制转换的情况下让 Typescript 从泛型推断出正确的类型?

[英]How can I get Typescript to infer the correct type from a generic without using cast?

The interpreter is smart enough to infer that the result of the filter function will be an array of strings, yet it thinks that is not assignable to the variable b of the same type?解释器足够聪明,可以推断过滤器 function 的结果将是一个字符串数组,但它认为这不能分配给相同类型的变量 b? Is there any way to solve this without being forced to do有什么办法可以解决这个问题而不必被迫这样做

const b: Arr<T> = arr.filter(() => true) as Arr<T>

在此处输入图像描述

This answer is not canon but I hope it helps build a robust enough mental model.这个答案不是佳能,但我希望它有助于建立一个足够强大的心理 model。

Breaking it down to multiple versions will disambiguate it a little bit:将其分解为多个版本会稍微消除歧义:

// I modified `Arr` for the sake of the explanation
type Arr<U> = U extends any ? U[] : U[]

function foo <T extends string>(arr: Arr<T>): T[] {
  const b = arr.filter(() => true);
  return b;
// ~~~~~~~~
// Type 'string[]' is not assignable to type 'T[]'.
//   Type 'string' is not assignable to type 'T'.
//     'string' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string'.
}

function bar <T extends string>(arr: T[]): Arr<T> {
  const b = arr.filter(() => true);
  return b;
// ~~~~~~~
// Type 'T[]' is not assignable to type 'Arr<T>'
}

function quux <T extends string>(arr: T[]): T[] {
  const b = arr.filter(() => true);
  return b;
}

TS can't process Arr<T> if T is a generic because its value is "not known" and therefore "there is no way" to tell if U extends any or not.如果T是泛型,TS 无法处理Arr<T> ,因为它的值“未知”,因此“没有办法”判断U any扩展。

Now you're going to tell me that TS knows that T extends string and should come to the conclusion that string does not extend any[] .现在你要告诉我 TS 知道T extends string并且应该得出string没有扩展any[]的结论。 It is even more ridiculous with my rewrite because everything extends any and we return U[] anyway.我的重写更加荒谬,因为一切都扩展了any并且我们无论如何都会返回U[]

TS takes neither the constraint of T nor the two arms of the conditional into account to make any kind of reasoning: either it decides that T is "not known" and gives up, or it has replaced T by its constraint at some point, then it will compute the return value but T is no longer a generic and is less useful. TS 既不考虑T的约束也不考虑条件的两个分支来进行任何类型的推理:要么它决定T是“未知的”并放弃,要么它在某个时刻用它的约束替换了T ,然后它将计算返回值,但T不再是通用的并且用处不大。

Now, what our examples highlight is that TS does not treat generics consistently:现在,我们的示例强调的是 TS 不会始终如一地对待 generics:

  • You can tell that in foo , it substituted T with its constraint, which explains that there is a disconnect between the input value and the return value.您可以看出,在foo中,它用约束替换了T ,这说明输入值和返回值之间存在脱节。

  • In the case of bar , TS did not do any substitution, it simply gave up and therefore cannot conclude that anything extends Arr<T> .bar的情况下,TS 没有做任何替换,它只是放弃了,因此不能得出任何扩展Arr<T>的结论。

So it appears that TS infers the type of each expression individually and does not propagate type information across the entire function. That's weird but if you think about it, it probably allows it to infer narrow return types more often than if it did.所以看起来 TS 单独推断每个表达式的类型,并且不会在整个 function 中传播类型信息。这很奇怪,但如果你仔细想想,它可能允许它更频繁地推断窄返回类型。

Interestingly, in your code TS would process Arr<T[]> because even though it does not know the value of T , it knows that it's wrapped in an array and can proceed to compare that with any[] and return the right type.有趣的是,在您的代码中,TS 会处理Arr<T[]>因为即使它不知道T的值,它也知道它被包装在一个数组中并且可以继续将其与any[]进行比较并返回正确的类型。 Here for some reason you designed Arr to flatten its argument so it would work although I don't recommend you do that.出于某种原因,在这里你设计Arr来展平它的论点,所以它会起作用,尽管我不建议你这样做。

If your use case is real, then quux is what you should do.如果您的用例是真实的,那么quux就是您应该做的。 I don't know why you designed Arr the way you did though.我不知道你为什么按照你的方式设计Arr

I also want to point out another inconsistency:我还想指出另一个不一致之处:

const id = <T>(x: T) => x;

function foobar <T extends string>(arr: Arr<T>): Arr<T> {
  const b = id(arr)
  return b;
}

Here foobar compiles.这里foobar编译。 I think the reason is because the type of arr.filter is the following:我认为原因是因为arr.filter的类型如下:

Array<T>.filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[]`

The generic is not on the function but on the object, which seems to force the resolution of arr , as if TS needed to know what Arr<T> was so that it can produce the type of Arr<T>['filter'] .泛型不在 function 上,而是在 object 上,这似乎强制解析arr ,就好像 TS 需要知道Arr<T>是什么,以便它可以生成Arr<T>['filter']的类型. This is just an assumption.这只是一个假设。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM