Ошибка ввода с помощью Either в TypeScript / fp-ts - PullRequest
0 голосов
/ 17 октября 2019

Я использую fp-ts, и у меня есть функция, которая возвращает либо объект HttpError, либо строку:

async getPreferencesForUserId(userId: string): Promise<Either<HttpResponseNotFound, string>> {
    const preferences = await getRepository(Preference).findOne({ userId });
    return preferences ? right(preferences.preferenceMap) : left(new HttpResponseNotFound({ code: 404, message: 'Could not find preferences' }));
  }

Я хотел бы вызвать эту функцию в другом файле, подобном этому:

const preferenceMapAsJsonStringOrError: Either<HttpResponseNotFound, string> = await this.preferenceService.getPreferencesForUserId(userId);

const response: HttpResponseOK | HttpResponseNotFound = pipe(preferenceMapAsJsonStringOrError, fold(
  e => e,
  r => new HttpResponseOK(r)
));
response.setHeader('content-type', 'application/json');

return response;

Вот как я бы это сделал в Scala. (за исключением того, что fold - это метод для типа Either, а не отдельная функция - поэтому здесь я использую помощник pipe)

Проблема в том, что я получаю ошибку отts-server:

Type 'HttpResponseOK' is missing the following properties from type 'HttpResponseNotFound': isHttpResponseNotFound, isHttpResponseClientError

И

node_modules/fp-ts/lib/Either.d.ts:129:69                                                                           
    129 export declare function fold<E, A, B>(onLeft: (e: E) => B, onRight: (a: A) => B): (ma: Either<E, A>) => B;
                                                                            ~~~~~~~~~~~
    The expected type comes from the return type of this signature.

Я могу обойти эту проблему, выполнив это гораздо более настоятельно:

const preferenceMapAsJsonStringOrError: Either<HttpResponseNotFound, string> = await this.preferenceService.getPreferencesForUserId(userId);
if (isLeft(preferenceMapAsJsonStringOrError)) {
  return preferenceMapAsJsonStringOrError.left;
}

const response = new HttpResponseOK(preferenceMapAsJsonStringOrError.right);
response.setHeader('content-type', 'application/json');

return response;

Но яв значительной степени теряет преимущество использования Either в этой точке.

1 Ответ

1 голос
/ 18 октября 2019

Проблема заключается в том, что с учетом того, как работает вывод TS, при использовании fold его тип возврата «фиксирован» с одним из первого аргумента (onLeft), а onRight не может «расширить»"it HttpResponseNotFound | HttpResponseOK.

Другими словами, вы не получите бесплатное объединение в общем случае с использованием TS и fp-ts.

Для этого конкретного сценария я бы предложил

  1. присвойте имя нужному типу объединения в выводе (не обязательно, но помогает в уточнении намерений):
type HttpResponse = HttpResponseNotFound | HttpResponseOK
явно "расширяет" возвращаемый тип сгиба. Это должно быть сделано вручную, путем аннотирования типа возврата аргумента onLeft fold:
const response: HttpResponse = pipe(
  preferenceMapAsJsonStringOrError,
  E.fold((e): HttpResponse => e, r => new HttpResponseOK(r))
)

или путем определения помощника widen следующим образом:

const widen = E.mapLeft<HttpResponse, HttpResponse>(e => e);

const response: HttpResponse = pipe(
  preferenceMapAsJsonStringOrError,
  widen,
  E.fold(identity, r => new HttpResponseOK(r))
);

Надеюсь, это поможет:)

...