Аргумент типа «число» не может быть назначен параметру типа «никогда» в массиве .include () - PullRequest
2 голосов
/ 15 февраля 2020

Я получаю ошибку машинописного текста из следующего кода. Предполагается, что метод includes() ожидает параметр 'never', а p.id - число. Кто-нибудь знает как это исправить?

export interface IProductPrice {
    id: number;
}
const [productPrices, setProductPrices] = useState<IProductPrice[]>([]);
const [selectedProductIds, setSelectedProductIds] = useState<string[] | number[]>([]);

const deleteSelectedProducts = (): void => {
        setProductPrices(productPrices.filter((p): boolean => !selectedProductIds.includes(p.id)));
        setSelectedProductIds([]);
};

Ответы [ 2 ]

3 голосов
/ 15 февраля 2020

Проблема

Когда у вас есть объединение массивов разных типов, вы не можете вызывать методы для них.

Причина

У вас есть string[] | number[], и поэтому .filter равно:

  ((filterFunc: (x: string) => boolean) => string[]) 
| ((filterFunc: (x: number) => boolean) => number[])

Когда TS объединяет эти подписи, он объединяет две filterFunc подписи вместе:

  ((x: number) => boolean) 
| ((x: string) => boolean)

Это немного не интуитивно понятно, но упрощается до (x: number & string) => boolean. Потому что, если у вас есть функция, которая принимает X, или функция, которая принимает Y, единственная безопасная вещь, которую нужно передать, это то, что является одновременно X и Y, или X & Y.

number & string однако, это невозможный тип, который «упрощается» до never. Поэтому подпись (x: never) => boolean.

Исправление

В идеале вы должны использовать только string[] или number[], но не оба. (Просто глядя на код, немного загадочно, почему id может быть только числом, но «идентификаторы выбранного продукта» тоже могут быть строками)


Но если вам нужно поддерживать обе строки и числа, самое простое решение здесь - использовать Array<string | number> вместо string[] | number[]: у одного типа массива нет проблемы объединения двух сигнатур .filter.

Вы можете изменить свое состояние на этот тип:

const [selectedProductIds, setSelectedProductIds] = useState<Array<string | number>>([]);

Это просто, но имеет недостаток в том, что он допускает массивы смешанных строк и чисел, которые могут быть желательно. (Например, setSelectProductIds(['0', 1, '2'])


Если нет, вы можете временно привести к Array<string | number>, выполнить фильтр, а затем вернуть к string[] | number[]. Это не супер чисто, но должно быть безопасно:

setProductPrices(
    (productPrices as Array<string | number>).filter((p): boolean => !selectedProductIds.includes(p.id)) as (string[] | number[])
);
2 голосов
/ 15 февраля 2020

Для вашего использованияState вместо

const [selectedProductIds, setSelectedProductIds] = useState<string[] | number[]>([]);

Можете ли вы попробовать

const [selectedProductIds, setSelectedProductIds] = useState< (string|number)[]>([]);

вместо?

Это потому, что, вероятно, отсутствует какой-то код, где вы установите selectedProductIds как массив строк. И в вашем коде вы строго определили его как 1 массив, а не как другой. Возможно, вышеупомянутое изменение исправляет это. Пожалуйста, подтвердите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...