Reputation: 377
The following code is fine but if you uncomment the last line ts will give an error. Seems like .filter() throws away the already inferred type from the previous step which doesn't make sense to me. Please explain how ts works here and suggest a solution.
type ItemIds =
| 'foo'
| 'bar';
interface Item {
id: ItemIds;
};
const items: Item[] = [
{
id: 'foo',
},
{
id: 'bar',
}
]
//.filter((item) => true)
Upvotes: 2
Views: 131
Reputation: 13807
It would maintain static typing if you were to declare your starting array as Item[]
, eg.:
const items: Item[] = [
{
id: 'foo',
},
{
id: 'bar',
}
]
const filteredItems: Item[] = items.filter(item => true)
The issue is with the string union type ItemIds
, as it's interpreted as string
without you telling the compiler otherwise.
The same issue comes up if you just let the compiler infer the type of items
first, then reassign it:
// This wont compile either.
const items = [
{
id: 'foo',
},
{
id: 'bar',
}
]
const typedItems: Item[] = items
Upvotes: 2
Reputation: 41240
It's because
[ { id: 'foo' }, { id: 'bar' }]
is still a { id: string } []
by the time you filter.
There's two approaches to this:
const items: Item[] = [
{
id: 'foo',
},
{
id: 'bar',
}
]
const filtered = items.filter((item) => true)
const items: Item[] = ([
{
id: 'foo',
},
{
id: 'bar',
}
] as Item[])
.filter((item) => true)
With the second approach you don't need to explicitly state the type since it will be implied already
const items = ([
{
id: 'foo',
},
{
id: 'bar',
}
] as Item[])
.filter((item) => true)
Upvotes: 1
Reputation: 1074148
When you assign the literal array to items
, even though the type of the literal is {id: string;}[]
(not [id: "foo" | "bar";]{}
aka Item[]
), TypeScript can see that all the id
values fit into "foo" | "bar"
so it allows the assignment to occur.
However, if you call .filter
on that array, it can't do that anymore. The type of the array is {id: string;}[]
, so that's the result of filter
. It can't know that filter
doesn't modify id
values, it just knows that the types for filter
say it will return an array of the same type. Which is {id: string;}[]
, not {id: "foo" | "bar";}[]
(Item[]
).
If you ensure that the array you're calling filter
on has the type Item[]
, it will work:
const items = ([
{
id: 'foo',
},
{
id: 'bar',
}
] as Item[])
.filter((item) => true);
Upvotes: 3
Reputation: 15674
You have to declare items
first
interface Item {
id: ItemIds;
};
const items: Item[] = [
{
id: 'foo',
},
{
id: 'bar',
}
]
const filtered = items.filter((item) => true) // filtered has type Item[]
Upvotes: 0