#typescript
Вопрос:
Следующий код подходит, но если вы раскомментируете последнюю строку, ts выдаст ошибку. Похоже на то .filter() отбрасывает уже выведенный тип из предыдущего шага, который для меня не имеет смысла. Пожалуйста, объясните, как здесь работает ts, и предложите решение.
type ItemIds =
| 'foo'
| 'bar';
interface Item {
id: ItemIds;
};
const items: Item[] = [
{
id: 'foo',
},
{
id: 'bar',
}
]
//.filter((item) => true)
Ответ №1:
Когда вы назначаете массив литералов items
, даже если тип литерала {id: string;}[]
(не [id: "foo" | "bar";]{}
aka Item[]
), TypeScript может видеть, что все id
значения вписываются в "foo" | "bar"
него, поэтому он позволяет выполнить назначение.
Однако, если вы вызовете .filter
этот массив, он больше не сможет этого делать. Тип массива таков {id: string;}[]
, так что это результат filter
. Он не может знать, что filter
это не изменяет id
значения, он просто знает, что типы, для filter
которых он говорит, вернут массив того же типа. То есть {id: string;}[]
, не {id: "foo" | "bar";}[]
( Item[]
).
Если вы убедитесь , что массив, к которому вы обращаетесь filter
, имеет соответствующий тип Item[]
, он будет работать:
const items = ([
{
id: 'foo',
},
{
id: 'bar',
}
] as Item[])
.filter((item) => true);
Ответ №2:
Это сохранит статическую типизацию , если вы объявите свой начальный массив Item[]
, например.:
const items: Item[] = [
{
id: 'foo',
},
{
id: 'bar',
}
]
const filteredItems: Item[] = items.filter(item => true)
Проблема связана с типом объединения строк ItemIds
, поскольку он интерпретируется так string
, что вы не сообщаете компилятору об обратном.
Та же проблема возникает, если вы просто позволите компилятору items
сначала определить тип, а затем переназначить его:
// This wont compile either.
const items = [
{
id: 'foo',
},
{
id: 'bar',
}
]
const typedItems: Item[] = items
Комментарии:
1. Мне очень нравится ваш пример в конце, в котором вы подчеркиваете, что TypeScript может проверять,
id
соответствуют ли все буквы s типу, только когда у него есть литерал для работы.
Ответ №3:
Это потому, что
[ { id: 'foo' }, { id: 'bar' }]
все еще { id: string } []
остается к тому времени, когда вы отфильтруете.
К этому есть два подхода:
- процесс после
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)
При втором подходе вам не нужно явно указывать тип, так как он уже будет подразумеваться
const items = ([
{
id: 'foo',
},
{
id: 'bar',
}
] as Item[])
.filter((item) => true)
Ответ №4:
Вы должны сначала заявить items
interface Item {
id: ItemIds;
};
const items: Item[] = [
{
id: 'foo',
},
{
id: 'bar',
}
]
const filtered = items.filter((item) => true) // filtered has type Item[]
Комментарии:
1. Ну, вам и не нужно. 🙂 Также было бы неплохо сказать, почему возникает проблема.