我实现了一个Result monad,几乎可以用:)
class Result<T> {
private readonly value: T;
private readonly error: Error;
constructor(value?: T, error?: Error) {
if (value) {
this.value = value;
}
if (error) {
this.error = error;
}
}
static ok<T>(ok: T): Result<T> {
return new Result<T>(ok);
}
static error<T>(err: Error): Result<T> {
return new Result<T>(undefined, err);
}
isOk(): boolean {
return this.value !== undefined;
}
isError(): boolean {
return this.error !== undefined;
}
chain<U>(f: (t: T) => Result<U>): Result<U> {
return this.isOk() ?
f(this.value) :
Result.error<U>(this.error);
}
map<U>(f: (t: T) => U): Result<U> {
return this.isOk() ?
Result.ok<U>(f(this.value)) :
Result.error<U>(this.error);
}
concat<U>(aResult: Result<U>): Result<(T | U)[]> {
return this.isOk() ?
Result.ok<(T | U)[]>(Array().concat(this.value, aResult.getValue())) :
Result.error(this.error);
}
getError(): Error {
return this.error;
}
getValue(): T {
return this.value;
}
}
这是您的用法:
const r1 = Result.ok(76)
.concat(Result.ok('ddd'))
.concat(Result.ok(true))
.map((i: (number | string | boolean)[]) => i);
在这里,我将三个结果连接起来:一个数字,一个字符串和一个布尔值。
之后,我想将它们映射并应用转换方法。
.map 方法应将键入的(数字|字符串|布尔值)[] 作为参数。
我知道问题出在哪里,但我现在知道如何解决。
问题与 .concat 方法有关:
concat<U>(aResult: Result<U>): Result<(T | U)[]> {
return this.isOk() ?
Result.ok<(T | U)[]>(Array().concat(this.value, aResult.getValue())) :
Result.error(this.error);
}
每次连接时都会调用此方法,如果您愿意,可以“递归”调用。
因此,在我的情况下,这是流程:
第一个连击- U 是字符串'ddd', T 是 76 数字。该方法返回(数字|字符串)[]
第二个连接- U 已经是(数字|字符串)[] ,并且需要一个布尔值。 Typescript而不是返回(number | string | boolean)[] ,而是返回(boolean |(number | string)[])[]
我可以使concat方法起作用并接受(string | number | boolean)[]签名吗?
有什么想法吗?
答案 0 :(得分:0)
如果T
已经是数组,则应返回(TElement | U)[]
。当this
具有数组类型的T
时,您可以添加额外的重载:
concat<TElement, U>(this: Result<TElement[]>, aResult: Result<U>): Result<(TElement | U)[]>
concat<U>(aResult: Result<U>): Result<(T | U)[]>
concat<U>(aResult: Result<U>): Result<(T | U)[]> {
return this.isOk() ?
Result.ok<(T | U)[]>(Array().concat(this.value, aResult.getValue())) :
Result.error(this.error);
}
或者您可以使用条件类型,该条件类型在调用站点将发挥相同的作用,但是您将需要在函数中进行类型声明,但是您可以减少以下重载:
concat<U>(aResult: Result<U>): Result<( (T extends (infer TElement)[] ? TElement : T ) | U)[]> {
return this.isOk() ?
Result.ok<(T | U)[]>(Array().concat(this.value, aResult.getValue())) :
Result.error(this.error) as any;
}