Stack Overflow Asked on November 17, 2020
This may be a duplicate question; however I can’t seem to find an answer, so please feel free to point me somewhere if it has already been answereded.
When working with generics, I find that typescript infers different types depending on whether I wrap a generic used as an array member. For example,
// 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>
Why is this the case? Is there something in the documentation I’m missing? More generically, can anyone point me to a good explanation of how typescript does inference of generics?
Look at the way that the code is written very closely.
// 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" } ]);
The way foo
's generic parameter is defined, T extends unknown
, means that T
can be anything, denoted by the unknown
, but it's 1 thing.
The actual function's parameter is Wrapped<T>[]
; breaking it down, foo
accepts an array of Wrapped<T>
, meaning it accepts a list of Wrapped
objects, but every one of them must have the same type, so it could accept [ { _i: 1 }, { _i: 2 }, { _i: 3 } ]
, or [ { _i: 'a' }, { _i: 'b' }, { _i: 'c' } ]
, but it cannot create a union/tuple as it's output, so something like [ { _i: 'a' }, { _i: 2 }, { _i: Symbol(NaN) } ]
would fail, because this type is actually Wrapped<string | number | symbol>[]
, whereas the former two were Wrapped<number>[]
, and Wrapped<string>[]
, respectively.
bar
, on the other hand, is defined as <T extends Wrapped<unknown>>
. In this case, T
is just an object of the type Wrapped
, without care to what it's type parameter is, as long as it has the general shape of a Wrapped
object.
Here, it can take an array of Wrapped
objects, regardless of whether they are all of the same types, or not.
Summing it up, foo
is accepting a contiguous array of the same type, whereas bar
is accepting an array of any Wrapped
objects.
Answered by xxh on November 17, 2020
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP