Пытаюсь оптимизировать код с условным выражением и не могу заставить его работать с [never].

Вопрос или проблема

У меня есть следующий код:

type Success<T> = { success: true, value: T }
type Failure<E> = { success: false, error: E }

type Result<T, E = never> = Success<T> | Failure<E>

function succeed<T>(value: T): Result<T, never> {
    return { success: true, value }
}
function fail<E>(error: E): Result<never, E> {
    return { success: false, error }
}

function test(value: string): Result<string, string> {
    if (value === undefined) {
        return fail("empty");
    } else {
        return succeed(value);
    }
}

Я пытаюсь оптимизировать это, чтобы, когда E является never, Result считался бы результатом Success.

Для этого я адаптировал определение Result с помощью следующего условного выражения:

type Result<T, E = never> = [E] extends [never]
  ? Success<T>
  : Success<T> | Failure<E>

Что вызывает ошибку TypeScript на result метода fail:

Тип ‘{ success: false; error: E; }’ не может быть присвоен типу ‘Result<never, E>’.

Я пытаюсь исправить это или найти альтернативы уже несколько дней, но не понимаю, почему условное выражение работает для succeed (когда E является never), и вызывает ошибку для общего случая.

Ответ или решение

Оптимизация кода с использованием условных выражений в TypeScript — задача, требующая глубокого понимания типизации в этом языке. Рассмотрим ваш запрос, связанный с типами Result, Success и Failure. Почему вы сталкиваетесь с ошибками при попытке адаптировать ваш код?

Понимание Исходного Кода

Исходная структура типов определяет две успешные и неуспешные формы результата:

  1. Success – обозначает успешный результат с обернутым значением типа T.
  2. Failure – обозначает неуспешный результат с обернутой ошибкой типа E.
  3. Result<T, E = never> – объединяющий тип, который может быть либо успешным, либо неуспешным. По умолчанию E имеет значение never.

Ваши функции succeed и fail создают результаты соответствующих типов.

Оптимизация Условного Выражения

Вы предложили изменить определение Result, используя условное выражение:

type Result<T, E = never> = [E] extends [never]
  ? Success<T>
  : Success<T> | Failure<E>;

Выражение работает нормально для успешного результата в функции succeed, однако, когда вы применяете его в функции fail, вы получаете ошибку:

Type '{ success: false; error: E; }' is not assignable to type 'Result<never, E>'.

Причины Ошибки

Ошибка возникает из-за использования условных типов, когда E принимается за never. В этом случае Result<T, E> в полной мере не соответствует типу Failure<E>, так как Result<never, E> всегда интерпретируется как Success<never>, и не может содержать поля ошибки.

Когда вы возвращаете fail, TypeScript ожидает получить результат типа Result<never, E>, но у вас возвращается Failure<E>, что приводит к конфликту типов.

Решение Проблемы

Чтобы устранить ошибку, можно воспользоваться следующей стратегией. Для обрабатывания случаев, когда E равен never, воспользуйтесь перегрузкой функции fail. Вот пример того, как можно переписать вашу реализацию:

function fail<E extends never>(error: E): Result<never, E> {
    return { success: false, error };
}

При этом, структура Result<T, E> могла бы выглядеть так:

type Result<T, E = never> = E extends never
  ? Success<T>
  : Success<T> | Failure<E>;

Заключение

Теперь структура Result гарантирует, что если E равен never, программа только имеет успешные результаты, при этом ошибка будет корректно обрабатываться во всех случаях. Эта реализация позволит избежать конфликта типов, сохраняя всю функциональность вашего кода.

При внесении изменений важно тщательно протестировать обновленный код, чтобы убедиться, что все сценарии работают как ожидалось. Это поможет сохранить стабильность и надежность вашей системы.

Оцените материал
Добавить комментарий

Капча загружается...