【问题标题】:Type error with Either in TypeScript/fp-ts在 TypeScript/fp-ts 中使用 Either 输入错误
【发布时间】:2019-10-17 15:31:07
【问题描述】:

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

【问题讨论】:

  • TS 的工作方式(以及折叠签名的编写方式)你不会在那里得到统一,在这里看到一个非常相似的讨论:github.com/gcanti/fp-ts/pull/945
  • 如果我只想要值或错误,那么fold 不适合使用吗?
  • 在下面起草了一个答案,如果有帮助请告诉我
  • 是的,昨天我坐下来思考的越多,我就越意识到 OF COURSE fold 期望同质类型。我不确定我在想什么。谢谢!

标签: typescript fp-ts


【解决方案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))
);

希望这会有所帮助:)

【讨论】:

  • 这看起来很棒!感谢您花时间解释这一点。
【解决方案2】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-16
    • 1970-01-01
    • 2020-04-25
    相关资源
    最近更新 更多