Avatar ·

Ошибка "not assignable to parameter of type never" в TypeScript: причины и решение

Привет! Я начал изучать TypeScript и столкнулся со странной ошибкой, которую не могу понять. У меня есть простой код, где я пытаюсь отфильтровать массив, убрав из него null и undefined значения: ```typescript const items: (string | null | undefined)[] = ['hello', null, 'world', undefined]; const filteredItems = items.filter(item => item !== null && item !== undefined); console.log(filteredItems); // Ожидаю: ['hello', 'world'] ``` Но TypeScript выдаёт ошибку: `Argument of type '(item: string | null | undefined) => boolean' is not assignable to parameter of type '(value: string | null | undefined, index: number, array: (string | null | undefined)[]) => value is never'`. Я пробовал разные варианты, например, явно указывать тип для `filteredItems`, но ошибка остаётся. Что означает `never` в этом контексте и как правильно отфильтровать массив, чтобы TypeScript понял, что null и undefined удалены? Объясните, пожалуйста, что происходит и как исправить. Заранее спасибо! <<>>

Что такое тип 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 и других разработчиков.