如果包装数组成员,为什么打字稿泛型会推断不同的类型?

Tom*_*mas 6 javascript typescript

这可能是一个重复的问题;但是我似乎找不到答案,所以如果已经有人回答了,请随时指出我的地方。

在使用泛型时,我发现 typescript 会根据我是否包装用作数组成员的泛型来推断不同的类型。例如,

// Set up
type Wrapped<I> = { _i: I };

// Not working example
function foo1<TInner extends unknown>(bar: Wrapped<TInner>[]) {
  return bar;
}
const a = foo1([{ _i: 5 }, { _i: "six" }]); // TInner = number, compiler complains about second array member despite the fact that it's inferring it

// Working example
function foo2<TInner extends Wrapped<unknown>>(bar: TInner[]) {
  return bar;
}
const b = foo2([{ _i: 5 }, { _i: "six" }]); // TInner = Wrapped<number> | Wrapped<string>
Run Code Online (Sandbox Code Playgroud)

为什么会这样?文档中是否有我遗漏的内容?更笼统地说,有人能指出我对打字稿如何推断泛型的一个很好的解释吗?

小智 0

仔细看看代码的编写方式。

// Slightly edited given example

type Wrapped<I> = { _i: I };

function foo<T extends unknown>(baz: Wrapped<T>[]) {}

foo1([ { _i: 5 }, { _i: "six" } ]);

function bar<T extends Wrapped<unknown>>(baz: T[]) {}

foo2([ { _i: 5 }, { _i: "six" } ]);
Run Code Online (Sandbox Code Playgroud)

定义了wayfoo的通用参数,T extends unknown,意味着T可以是任何东西,用 表示unknown,但它是1件事。

实际函数的参数是Wrapped<T>[];将其分解,foo接受一个数组Wrapped<T>,这意味着它接受一个对象列表Wrapped,但每个对象都必须具有相同的类型,因此它可以接受[ { _i: 1 }, { _i: 2 }, { _i: 3 } ],或[ { _i: 'a' }, { _i: 'b' }, { _i: 'c' } ],但它不能创建一个联合/元组作为它的输出,所以像[ { _i: 'a' }, { _i: 2 }, { _i: Symbol(NaN) } ]会失败,因为这种类型实际上是Wrapped<string | number | symbol>[],而前两种类型分别是Wrapped<number>[]、 和Wrapped<string>[]

bar另一方面,定义为<T extends Wrapped<unknown>>。在这种情况下,T就是 类型的对象Wrapped,不关心它的类型参数是什么,只要它具有对象的一般形状即可Wrapped。在这里,它可以采用对象数组Wrapped,无论它们是否都属于相同类型。

总结一下,foo接受相同类型的连续数组,而bar接受任何对象的数组Wrapped

  • 老实说,现在我看它,这是一个糟糕的解释,因为对我来说,它只是“有道理”,没有经过思考。 (2认同)