功能打字稿错误消息有助于干净的架构项目

问题描述 投票:0回答:1

我正在使用打字稿进行函数式编程,并将其用于一个新项目,但遇到了一些障碍。对于下面的代码,我将其用于干净的架构模型,但收到打字稿错误,但不确定它告诉我问题所在。任何帮助我理解如何解决这个问题的帮助将不胜感激。

  1. 类型“TaskEither”不可分配给类型“TaskEither>”。
  • 空洞可能从何而来?我应该如何修复它?
  1. “(country: string) => TaskEither>”类型的参数不可分配给“(b: Either) => TaskEither>”类型的参数。 参数“country”和“b”的类型不兼容。
  • “b:Either”从哪里来,我应该如何修复它?

代码部分下方列出了完整的错误消息。

  import { pipe } from 'fp-ts/lib/function';
  import { Option } from "fp-ts/Option";
  import { some } from "fp-ts/Option";
  import { Either } from 'fp-ts/lib/Either';
  import * as E from 'fp-ts/lib/Either';
  import { TaskEither, left, right, chain, fromEither } from 'fp-ts/lib/TaskEither';

  type NotFoundError = { kind: string; message: string };
  type DomainError = { kind: string, message: string };



  type UserRepository = {
    retrieveUsers: (page: number) => (name: string) => (street: string) => (city: string) => (state: string) => (zipCode: string) => (country: string) => TaskEither<Error, Option<User[]>>;
  }

  export const makeUserRepository = (client: Client): UserRepository => {
    const retrieveUsers = (page) => (name) => (street) => (city) => (state) => (zipCode) => (country): TaskEither<Error, Option<User[]>> => tryCatch(
        async () => {
            let queryString = '';
            let paramList = [];
            let queryScenarios = [];
            let counter = 0;

            queryString += `
            SELECT c."name", a."userId", a."companyId", a."city", a."state", a."zipCode", a."street", a."country"
            FROM profile."User" a INNER JOIN profile."Company" c ON (a."companyId" = c."companyId")
          `;

            if (name && name.length > 0) {
                queryScenarios.push('(c.name like %$' + (++counter) + '%)');
                paramList.push(name);
            }

            if (street && street.length > 0) {
                queryScenarios.push('a."street" like %$' + (++counter) + '%');
                paramList.push(street);
            }

            if (city && city.length > 0) {
                queryScenarios.push('a."city" like %$' + (++counter) + '%');
                paramList.push(city);
            }

            if (state && state.length > 0) {
                queryScenarios.push('a."state" like %$' + (++counter) + '%');
                paramList.push(state);
            }

            if (zipCode && zipCode.length > 0) {
                queryScenarios.push('a."zipCode" like %$' + (++counter) + '%');
                paramList.push(zipCode);
            }

            if (country && country.length > 0) {
                queryScenarios.push('a."country" like %$' + (++counter) + '%');
                paramList.push(country);
            }

            queryString += " LIMIT $" + (++counter) + " OFFSET $" + (++counter);
            paramList.push((isValidNumber(process.env.MAX_PAGE_SIZE) ? parseInt(process.env.MAX_PAGE_SIZE) : 0));
            paramList.push((isValidNumber(process.env.MAX_PAGE_SIZE) ? parseInt(process.env.MAX_PAGE_SIZE) : 0) * (page - 1));

            const result: QueryResult<User> = await client.query(queryString, paramList);
            result.rows.length > 0 ? some(result.rows) : none;
        },
        (reason) => new Error(String(reason))
    );

    return {
        retrieveUsers
    }
}


const validatePageNameFields = (page: number) => (name: string) => (street: string) => (city: string) => (state: string) => (zipCode: string) => (country: string): Either<DomainError, string> => {
    const schema = Joi.object().keys({
        name: Joi.string().min(4).max(50).allow(null),
        page: Joi.number().required(),
        street: Joi.string().min(8).max(75).allow(null),
        city: Joi.string().min(1).max(50).allow(null),
        state: Joi.string().min(1).max(50).allow(null),
        zipCode: Joi.string().min(5).max(10).allow(null),
        country: Joi.string().min(5).max(25).allow(null),
    });

    let {error} = schema.validateAsync({page, name, street, city, state, zipCode, country});
    if (error && error.length > 0)
        return E.left({ kind: 'ValidationError', message: error });

    return E.right(country);
};

type UserInteractor = {
    retrieveUserList: (page: number) => (name: string) => (street: string) => (city: string) => (state: string) => (zipCode: string) => (country: string) => TaskEither<Error | NotFoundError, Option<User[]>>;
};

export const makeUserInteractor = (repository: ReturnType<typeof makeUserRepository>): UserInteractor => {
    const retrieveUserList = (page) => (name) => (street) => (city) => (state) => (zipCode) => (country): TaskEither<Error | NotFoundError, Option<User[]>> => {
        return pipe(
            country,
            validatePageNameFields(page)(name)(street)(city)(state)(zipCode),
            repository.retrieveUsers(page)(name)(street)(city)(state)(zipCode),
            chain((maybeUser) => {
                if (maybeUser._tag === 'None') {
                    return left({ kind: 'NotFoundError', message: 'User not found.' });
                }
                return right(some(maybeUser.value));
            })
        );
    };
}


error TS2322: Type 'TaskEither<Error, void>' is not assignable to type 'TaskEither<Error, Option<User[]>>'.
Type 'void' is not assignable to type 'Option<User[]>'.

const retrieveUsers = (page) => (name) => (street) => (city) => (state) => (zipCode) => (country): TaskEither<Error, Option<User[]>> => tryCatch(
~~~~~~~~~
    async () => {
~~~~~~~~~~~~~~~~~~~~~
...
(reason) => new Error(String(reason))
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    );
~~~~~


error TS2345: Argument of type '(country: string) => TaskEither<Error, Option<User[]>>' is not assignable to parameter of type '(b: Either<DomainError, string>) => TaskEither<Error, Option<User[]>>'.
Types of parameters 'country' and 'b' are incompatible.
Type 'Either<DomainError, string>' is not assignable to type 'string'.
Type 'Right<string>' is not assignable to type 'string'.

repository.retrieveUsers(page)(name)(street)(city)(state)(zipCode),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
node.js typescript express functional-programming functional-dependencies
1个回答
0
投票

您遇到的错误是因为

tryCatch
函数中的
retrieveUsers
函数在成功情况下没有返回任何值。它应该返回
some(result.rows)
以匹配返回类型
TaskEither<Error, Option<User[]>>
。另外,您需要确保返回
tryCatch
函数的结果。这是更正后的代码:

import { pipe } from 'fp-ts/lib/function';
import { Option, some, none } from 'fp-ts/Option';
import { TaskEither, tryCatch, left, right, chain } from 'fp-ts/TaskEither';
import { QueryResult } from 'your-query-result-type'; // Import your QueryResult type

// ...

const retrieveUsers = (
  page: number,
  name: string,
  street: string,
  city: string,
  state: string,
  zipCode: string,
  country: string
): TaskEither<Error, Option<User[]>> =>
  tryCatch(
    async () => {
      let queryString = '';
      let paramList = [];
      let queryScenarios = [];
      let counter = 0;

      queryString += `
        SELECT c."name", a."userId", a."companyId", a."city", a."state", a."zipCode", a."street", a."country"
        FROM profile."User" a INNER JOIN profile."Company" c ON (a."companyId" = c."companyId")
      `;

      if (name && name.length > 0) {
        queryScenarios.push('(c.name like $' + (++counter) + ')');
        paramList.push('%' + name + '%');
      }

      if (street && street.length > 0) {
        queryScenarios.push('a."street" like $' + (++counter) + '');
        paramList.push('%' + street + '%');
      }

      // Add similar checks for other parameters...

      queryString += ' LIMIT $' + (++counter) + ' OFFSET $' + (++counter);
      paramList.push(
        isValidNumber(process.env.MAX_PAGE_SIZE)
          ? parseInt(process.env.MAX_PAGE_SIZE)
          : 0
      );
      paramList.push(
        (isValidNumber(process.env.MAX_PAGE_SIZE)
          ? parseInt(process.env.MAX_PAGE_SIZE)
          : 0) *
          (page - 1)
      );

      const result: QueryResult<User> = await client.query(queryString, paramList);
      return result.rows.length > 0 ? some(result.rows) : none;
    },
    (reason) => new Error(String(reason))
  );

// ...

tryCatch
函数中,我在
return
之前添加了
result.rows.length > 0 ? some(result.rows) : none;
语句,以确保函数按预期返回
Option<User[]>
类型。另外,在
retrieveUsers
中调用时,请确保将所有必需的参数传递给
makeUserInteractor

错误消息表明代码中存在某些类型不匹配。具体来说,

retrieveUsers
函数返回
TaskEither<Error, void>
类型而不是
TaskEither<Error, Option<User[]>>
。此外,
validatePageNameFields
函数返回
Either<DomainError, string>
类型,而不是
(page: number) => (name: string) => (street: string) => (city: string) => (state: string) => (zipCode: string) => (country: string) => Either<DomainError, string>

要解决这些问题,您可以修改代码如下:

  1. retrieveUsers
    函数中,您需要返回
    some(result.rows)
    ,而不是仅仅调用
    none
    some(result.rows)

  2. validatePageNameFields
    函数中,您需要返回一个接受所有参数的函数,而不是仅仅返回
    E.right(country)

这是修改后的代码:

const retrieveUsers = (page) => (name) => (street) => (city) => (state) => (zipCode) => (country): TaskEither<Error, Option<User[]>> => tryCatch(
  async () => {
    let queryString = '';
    let paramList = [];
    let queryScenarios = [];
    let counter = 0;
    queryString += ` SELECT c."name", a."userId", a."companyId", a."city", a."state", a."zipCode", a."street", a."country" FROM profile."User" a INNER JOIN profile."Company" c ON (a."companyId" = c."companyId") `;
    if (name && name.length > 0) {
      queryScenarios.push('(c.name like %$' + (++counter) + '%)');
      paramList.push(name);
    }
    if (street && street.length > 0) {
      queryScenarios.push('a."street" like %$' + (++counter) + '%');
      paramList.push(street);
    }
    if (city && city.length > 0) {
      queryScenarios.push('a."city" like %$' + (++counter) + '%');
      paramList.push(city);
    }
    if (state && state.length > 0) {
      queryScenarios.push('a."state" like %$' + (++counter) + '%');
      paramList.push(state);
    }
    if (zipCode && zipCode.length > 0) {
      queryScenarios.push('a."zipCode" like %$' + (++counter) + '%');
      paramList.push(zipCode);
    }
    if (country && country.length > 0) {
      queryScenarios.push('a."country" like %$' + (++counter) + '%');
      paramList.push(country);
    }
    queryString += " LIMIT $" + (++counter) + " OFFSET $" + (++counter);
    paramList.push((isValidNumber(process.env.MAX_PAGE_SIZE) ? parseInt(process.env.MAX_PAGE_SIZE) : 0));
    paramList.push((isValidNumber(process.env.MAX_PAGE_SIZE) ? parseInt(process.env.MAX_PAGE_SIZE) : 0) * (page - 1));
    const result: QueryResult<User> = await client.query(queryString, paramList);
    return result.rows.length > 0 ? some(result.rows) : none;
  },
  (reason) => new Error(String(reason))
);

const validatePageNameFields = (page: number) => (name: string) => (street: string) => (city: string) => (state: string) => (zipCode: string) => (country: string): Either<DomainError, string> => {
  const schema = Joi.object().keys({
    name: Joi.string().min(4).max(50).allow(null),
    page: Joi.number().required(),
    street: Joi.string().min(8).max(75).allow(null),
    city: Joi.string().min(1).max(50).allow(null),
    state: Joi.string().min(1).max(50).allow(null),
    zipCode: Joi.string().min(5).max(10).allow(null),
    country: Joi.string().min(5).max(25).allow(null),
  });

  return pipe(
    schema.validateAsync({page, name, street, city, state, zipCode, country}),
    E.map(() => (page) => (name) => (street) => (city) => (state) => (zipCode) => (country))
  );
};

这些更改应该可以修复代码中的类型错误。

© www.soinside.com 2019 - 2024. All rights reserved.