This code fails with the error in the title:
type Foo = {
foo: number
items: Foo[]
}
type Bar = {
bar: number
items: Foo[]
}
function func(list: Bar[], indices: number[]) {
let cur: Foo[] | Bar[] = list
for (const index of indices) {
const item = cur[index] // error here
cur = item.items
}
}
Why is this happening? cur
has an explicitly defined type. So does index
. It appears that flow analysis determined that cur
is actually Bar[]
and errors out when I assign anything else to it?
If I change the definition of cur
to let cur = list as Foo[] | Bar[]
then there is no error, because it never changes to a different type. But this doesn't look good since the as
statement might obscure errors if definitions change later on.
This code fails with the error in the title:
type Foo = {
foo: number
items: Foo[]
}
type Bar = {
bar: number
items: Foo[]
}
function func(list: Bar[], indices: number[]) {
let cur: Foo[] | Bar[] = list
for (const index of indices) {
const item = cur[index] // error here
cur = item.items
}
}
Why is this happening? cur
has an explicitly defined type. So does index
. It appears that flow analysis determined that cur
is actually Bar[]
and errors out when I assign anything else to it?
If I change the definition of cur
to let cur = list as Foo[] | Bar[]
then there is no error, because it never changes to a different type. But this doesn't look good since the as
statement might obscure errors if definitions change later on.
1 Answer
Reset to default 1The problem with the dynamic type change by cur = item.items
. I suggest to narrow the type to the common items
prop:
Playground
type Foo = {
foo: number
items: Foo[]
}
type Bar = {
bar: number
items: Foo[]
}
function func(list: Bar[], indices: number[]) {
let cur: {items: Foo[]}[] = list;
for (const index of indices) {
const item = cur[index] // error here
cur = item.items
}
}
You could also do:
let cur: Pick<Foo, 'items'>[] = list;
item
depends oncur
and the type ofcur
depends onitem
due to control flow analysis. A human being can analyze the types in a way that avoids this circularity, but TS has an inference algorithm and can't arbitrarily deviate from it. See ms/TS#45213. You can sidestep the problem by annotating (not usingas
)item
(const item: Foo | Bar =
). Does this fully address the question? If so I'll write an answer or find a duplicate. If not, what am I missing? – jcalz Commented Mar 15 at 21:59