我正在使用打字稿进行函数式编程,并将其用于一个新项目,但遇到了一些障碍。对于下面的代码,我将其用于干净的架构模型,但收到打字稿错误,但不确定它告诉我问题所在。任何帮助我理解如何解决这个问题的帮助将不胜感激。
代码部分下方列出了完整的错误消息。
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),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
您遇到的错误是因为
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>
。
要解决这些问题,您可以修改代码如下:
在
retrieveUsers
函数中,您需要返回some(result.rows)
,而不是仅仅调用none
或some(result.rows)
。
在
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))
);
};
这些更改应该可以修复代码中的类型错误。