Итак, ваша проблема выглядит так:
type Fruit = "Apple" | "Pear";
interface FruitFilter {
fruits: Fruit[];
}
declare function Fruits(filter: FruitFilter): boolean;
Fruits({ fruits: ["Apple", "Apple", "Pear"] }); // okay
Fruits({ fruits: ["Apple", "App1e", "Pear"] }); // error
// actual error: ~~~~~~~ ~~~~~~~ ~~~~~~ <-- string not assignable to Fruit
// expected error: ~~~~~~~ <-- "App1e" not assignable to Fruit
Дело не в том, что у вас ошибка, а в том, что ошибка не ограничена "плохими" элементами массива.
Мое предположение о том, почему это происходит, заключается в том, что компилятор имеет тенденцию расширять строковые литералы до string
, а типы кортежей - до массивов, если вы не дадите ему подсказки не делать этого. Поэтому, когда он не может проверить, что fruits
имеет тип Fruit[]
, он выполняет резервное копирование и просматривает то, что вы ему дали. Он расширяется от ["Apple", "App1e", "Pear"]
до string[]
(забывая как о строковых литералах, так и о том, что это трехэлементный кортеж), понимает, что string[]
нельзя назначить Fruit[]
, а затем продолжает предупреждать вас об этом пометив каждый элемент. Я провел краткий поиск проблем с GitHub , чтобы узнать, было ли об этом когда-либо сообщено, но я этого не видел. Возможно, стоит что-то подать.
В любом случае, чтобы проверить свои предположения, я решил изменить объявление Fruits()
, чтобы намекнуть, что нам нужен кортеж строковых литералов, если это вообще возможно. Обратите внимание, что [в настоящее время нет удобного способа сделать это]; способы намека прямо сейчас алхимические:
// ?⚗??❓
declare function Fruits2<S extends string, T extends S[] | [S]>(arr: {
fruits: T & { [K in keyof T]: Fruit };
}): boolean;
Fruits2({ fruits: ["Apple", "Apple", "Pear"] }); // okay
Fruits2({ fruits: ["Apple", "App1e", "Pear"] }); // error
// ~~~~~~~ <--string is not assignable to never
Хорошо, размещение этой ошибки там, где вы хотите, хотя сообщение, возможно, все еще сбивает с толку. Вот что происходит, когда компилятор пытается присвоить "Apple"
пересечению Fruit & "App1e"
, которого не существует. Компилятор корректно уменьшает Fruit & "App1e"
до never
..., но, возможно, слишком рано, чтобы сообщение об ошибке было полезным.
В любом случае, я не рекомендую это «решение», так как оно намного сложнее и дает вам несколько лучший опыт ошибок в ситуациях с ошибками. Но, по крайней мере, это что-то вроде ответа о том, почему это происходит, вместе с возможным указанием того, как его решить (например, найти или подать вопрос об этом). Хорошо, удачи!
Ссылка на код