示例说明
异常适合不可预期的程序性错误,业务失败混进异常流会丢失类型信息且强制所有调用方 try-catch。Result 联合把失败变成普通返回值:ok 标志判别分支,错误对象保持类型,可像数据一样传递、缓存与映射。mapResult 这类组合子让链式变换在错误时自动短路,金字塔回调消失。
技术要点
- ok 判别分支保持类型收窄
- 错误类型参数化保留具体错误类别
- 组合子在错误路径自动短路
代码解读
fetchUser 对非法 ID 返回 ValidationError 类型的错误分支,HTTP 失败返回通用错误;两者都通过 err 构造器装箱。mapResult 只在成功分支执行变换函数,错误原样透传。消费端的三分支演示按错误类型分类处理:字段校验问题、上游失败各有归口。
使用建议
不要用 Result 表达宕机级错误,那仍该抛出。组合子按需增加,别造大型函数式库。与 Promise.all 组合时注意错误汇聚策略。
代码示例
type Result<T, E = Error> =
| { ok: true; value: T }
| { ok: false; error: E };
const ok = <T>(value: T): Result<T, never> => ({ ok: true, value });
const err = <E>(error: E): Result<never, E> => ({ ok: false, error });
class ValidationError {
constructor(readonly field: string, readonly reason: string) {}
}
async function fetchUser(id: string): Promise<Result<{ id: string; email: string }, Error>> {
if (!/^[a-z0-9-]+$/.test(id)) {
return err(new ValidationError('id', 'invalid characters'));
}
const res = await fetch('https://api.example.com/users/' + id);
if (!res.ok) {
return err(new Error('upstream status ' + res.status));
}
return ok(await res.json());
}
async function mapResult<T, U, E>(
result: Result<T, E>, fn: (value: T) => Promise<U>,
): Promise<Result<U, E>> {
if (!result.ok) return result;
return ok(await fn(result.value));
}
const user = await fetchUser('u-123');
const decorated = await mapResult(user, async (u) => ({ ...u, email: u.email.toUpperCase() }));
if (decorated.ok) {
console.log('email:', decorated.value.email);
} else if (decorated.error instanceof ValidationError) {
console.log('field problem:', decorated.error.field);
} else {
console.log('failure:', decorated.error.message);
}