0

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

4

2 回答 2

4

问题在于,考虑到 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))
);

希望这可以帮助 :)

于 2019-10-18T09:49:41.853 回答
1

尝试这两种方法后,我仍然会遇到类型错误。为我解决的问题是明确指定折叠的类型。

fold<HttpResponseNotFound, HttpResponseOK, HttpResponseNotFound | HttpResponseOK>(
  e => e,
  r => new HttpResponseOK(r)
)
于 2020-02-15T23:27:07.003 回答