异常的联合类型
Union Type for Exceptions
我正在寻找可能会滥用 TypeScript 类型系统的解决方案。
我有一个为存储库提供端口的服务(存储库必须实现的接口),因为该服务不能知道存储库的具体实现。由于这个事实,接口还必须提供服务可以处理的错误的定义。
为此我使用 ts-results.
我可以轻松地将错误定义为字符串,但我想在出现错误时从存储库向服务提供一些进一步的信息。
所以我尝试将错误定义为各种错误的联合类型 类。问题是每个默认错误都与更专业的错误的签名相匹配。
因此,在存储库中,无法阻止将任何其他错误传递给服务(端口)。
// The Port Definition
export abstract class FriendshipRepositoryPort implements IFriendshipRepository {
abstract load(
userIdA: UserId, userIdB: UserId
): Promise<Result<IFriendshipAggregate, FriendshipRepositoryErrors>>;
abstract persist(
friendship: IFriendshipAggregate
): Promise<Result<void, FriendshipRepositoryErrors>>;
}
// repo implementation
async persist(friendship: IFriendshipAggregate): Promise<Result<void, FriendshipRepositoryErrors>> {
// ... preparing persisting the entities
try {
return new Ok(await this._persistenceManager.execute(querySpec));
} catch (e) {
console.error(e);
// FIXME: This should not be possible!
return new Err(new RuntimeError());
}
}
// error definition
export type FriendshipRepositoryErrors = UserNotFoundError
| DomainRuleViolation
| DatabaseWriteError
| DatabaseReadError;
是否有任何方法可以将结果定义为仅接受给定 类(或继承人)的实例作为错误类型?
我还创建了一个 playground 来在一个非常小的例子中演示这个问题。
我认为这里最好的解决方案是使用discriminated union types。
虽然这看起来很麻烦,但它可能是最安全和明确的方式来告诉可能会发生什么错误,更具体地说,哪些错误可以作为错误注入到您的结果中。
查看this playglound编译器的行为方式。
class ErrorA extends Error{
#type = ErrorA.name;
}
class ErrorB extends Error {
#type = ErrorB.name;
}
class NotAllowedError extends Error {
}
class ErrorC extends ErrorB {
#type = ErrorC.name;
}
type AllowedErrorTypes = ErrorA | ErrorB;
function handleError(error: AllowedErrorTypes): void {
if(error instanceof ErrorA) {
throw error;
}
if(error instanceof ErrorB) {
throw error;
}
// no other options left.
// If I add another allowedError the compiler is going to complain
const exhausted: never = error;
}
handleError(new ErrorA());
handleError(new ErrorB());
handleError(new NotAllowedError());
handleError(new ErrorC());
我正在寻找可能会滥用 TypeScript 类型系统的解决方案。
我有一个为存储库提供端口的服务(存储库必须实现的接口),因为该服务不能知道存储库的具体实现。由于这个事实,接口还必须提供服务可以处理的错误的定义。
为此我使用 ts-results.
我可以轻松地将错误定义为字符串,但我想在出现错误时从存储库向服务提供一些进一步的信息。 所以我尝试将错误定义为各种错误的联合类型 类。问题是每个默认错误都与更专业的错误的签名相匹配。
因此,在存储库中,无法阻止将任何其他错误传递给服务(端口)。
// The Port Definition
export abstract class FriendshipRepositoryPort implements IFriendshipRepository {
abstract load(
userIdA: UserId, userIdB: UserId
): Promise<Result<IFriendshipAggregate, FriendshipRepositoryErrors>>;
abstract persist(
friendship: IFriendshipAggregate
): Promise<Result<void, FriendshipRepositoryErrors>>;
}
// repo implementation
async persist(friendship: IFriendshipAggregate): Promise<Result<void, FriendshipRepositoryErrors>> {
// ... preparing persisting the entities
try {
return new Ok(await this._persistenceManager.execute(querySpec));
} catch (e) {
console.error(e);
// FIXME: This should not be possible!
return new Err(new RuntimeError());
}
}
// error definition
export type FriendshipRepositoryErrors = UserNotFoundError
| DomainRuleViolation
| DatabaseWriteError
| DatabaseReadError;
是否有任何方法可以将结果定义为仅接受给定 类(或继承人)的实例作为错误类型?
我还创建了一个 playground 来在一个非常小的例子中演示这个问题。
我认为这里最好的解决方案是使用discriminated union types。 虽然这看起来很麻烦,但它可能是最安全和明确的方式来告诉可能会发生什么错误,更具体地说,哪些错误可以作为错误注入到您的结果中。
查看this playglound编译器的行为方式。
class ErrorA extends Error{
#type = ErrorA.name;
}
class ErrorB extends Error {
#type = ErrorB.name;
}
class NotAllowedError extends Error {
}
class ErrorC extends ErrorB {
#type = ErrorC.name;
}
type AllowedErrorTypes = ErrorA | ErrorB;
function handleError(error: AllowedErrorTypes): void {
if(error instanceof ErrorA) {
throw error;
}
if(error instanceof ErrorB) {
throw error;
}
// no other options left.
// If I add another allowedError the compiler is going to complain
const exhausted: never = error;
}
handleError(new ErrorA());
handleError(new ErrorB());
handleError(new NotAllowedError());
handleError(new ErrorC());