Ошибка "not assignable to parameter of type never" в TypeScript: причины и решение
Что такое тип never и почему возникает ошибка?
Тип never в TypeScript обозначает значение, которое никогда не может произойти. В контексте вашей ошибки TypeScript пытается вывести тип возвращаемого значения метода filter, но из-за особенностей вывода типов он "схлопывает" все возможные типы в never. Это происходит потому, что компилятор не может автоматически определить, что после фильтрации массив будет содержать только строки.
Причина проблемы
Метод Array.filter() в TypeScript имеет две сигнатуры:
filter<S extends T>(predicate: (value: T, index: number, array: T[]) => value is S, thisArg?: any): S[]
filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[]
- Первая — с пользовательским защитником типа (type guard), возвращающим
value is S. - Вторая — обычный предикат, возвращающий
boolean.
Когда вы используете стрелочную функцию без явного указания защитника типа, TypeScript пытается использовать первую сигнатуру. Но так как предикат item !== null && item !== undefined возвращает boolean, а не value is S, компилятор не может вывести тип S и присваивает ему never.
Как исправить: 4 способа
Способ 1. Использовать защитник типа (type guard) вручную
const filteredItems = items.filter(
(item): item is string => item !== null && item !== undefined
);
// filteredItems: string[]
Способ 2. Использовать встроенную функцию Boolean
const filteredItems = items.filter(Boolean);
// filteredItems: string[]
Работает, потому что Boolean является защитником типа для всех truthy значений.
Способ 3. Использовать flatMap с проверкой
const filteredItems = items.flatMap(item => item ? [item] : []);
// filteredItems: string[]
Способ 4. Явно указать generic-параметр
const filteredItems = items.filter<string>(item => item !== null && item !== undefined);
// filteredItems: string[]
Дополнительное объяснение про never
Ошибка "not assignable to parameter of type never" возникает, когда TypeScript не может найти подходящий тип для обобщённого параметра. В вашем случае S в сигнатуре filter<S extends T> выводится как never, потому что:
- Вы не указали
Sявно - Предикат не является защитником типа
- TypeScript не может вывести
Sиз контекста
Тип never — это "дно" иерархии типов, которому нельзя присвоить никакое значение, поэтому возникает ошибка.
Рекомендую использовать способ 1 (явный защитник типа) — он наиболее читаемый и явно показывает намерение разработчика.
<<Альтернативное решение: глобальный хелпер для фильтрации
Если вы часто фильтруете null/undefined, создайте вспомогательную функцию:
// helpers.ts
export function isNotNull<T>(value: T | null | undefined): value is T {
return value !== null && value !== undefined;
}
// main.ts
const items: (string | null | undefined)[] = ['hello', null, 'world', undefined];
const filteredItems = items.filter(isNotNull);
// filteredItems: string[]
Этот подход особенно полезен в больших проектах, где такая фильтрация встречается часто. Вы также можете комбинировать несколько проверок:
const mixed: (number | string | null)[] = [1, 'a', null, 2, 'b'];
const numbers = mixed.filter((v): v is number => typeof v === 'number');
// numbers: number[]
Главное преимущество — переиспользуемость и явное указание типов, что делает код более предсказуемым для TypeScript и других разработчиков.