在TypeScript / fp-ts中使用Either键入错误

时间:2019-10-17 15:31:07

标签: typescript fp-ts

我正在使用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的好处。

2 个答案:

答案 0 :(得分:1)

问题在于,考虑到TS推理的工作原理,使用fold时,其返回类型将“固定”到第一个参数(onLeft)和onRight中的一个无法HttpResponseNotFound | HttpResponseOK“扩展”它。

换句话说,在通常情况下,使用TS和fp-ts不会免费获得统一。

对于这种特定情况,我建议

  1. 为您要在输出中使用的联合类型命名(并非绝对必要,但有助于阐明意图):
type HttpResponse = HttpResponseNotFound | HttpResponseOK
  1. 明确地“扩展”折叠的返回类型。必须通过注释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))
);

希望这会有所帮助:)

答案 1 :(得分:0)

尝试2种方法后,我仍然收到类型错误。对我来说固定的是明确指定折叠的类型。

fold<HttpResponseNotFound, HttpResponseOK, HttpResponseNotFound | HttpResponseOK>(
  e => e,
  r => new HttpResponseOK(r)
)