refactor(data): move Result utility to utils and simplify API

Relocate the Result type from `@/data/result` to `@/utils/result` and
replace the discriminated-union API (`kind`/`value`) with a simpler
boolean-based API (`success`/`data`) across all repositories and
storage classes for improved readability and consistency.
This commit is contained in:
2026-06-09 10:34:40 +08:00
parent c767322db6
commit 904cb3638a
16 changed files with 81 additions and 122 deletions
+76
View File
@@ -0,0 +1,76 @@
/**
* 仓库层统一 Result<T>
*
* 与 `src/data/storage/result.ts` 的 `Result<T>``{kind, value}` 形态)并存:
* - 本类型供仓库层(`src/data/repositories/*`)使用,`{success, data}` 形态更贴近
* 原 Dart `Result.success` / `Result.failure` 命名与 API 端 envelope 风格。
* - 存储层继续使用 `{kind, value}`,因为它的消费者(localStorage、Dexie)已经在
* 大量代码中 pattern-match `r.kind`。
* - 两个类型路径不同(`@/data/result` vs `@/data/storage/result`),不冲突;
* 如需在同模块中混用,可用 `import { Result as StorageResult } from
* "@/data/storage/result"` 别名。
*
* `error` 固定为 `Error`(非 `unknown`):HTTP 拦截器与共享 unwrap 都抛 `ApiError`
* `ApiError extends Error`),仓库 `Result.wrap` 捕获后用 `toError` 规范化,
* 调用方能可靠地 `instanceof ApiError` / `error.message` 访问错误信息。
*/
export type Result<T> =
| { readonly success: true; readonly data: T }
| { readonly success: false; readonly error: Error };
export const Result = {
/** 构造成功结果。 */
ok<T>(data: T): Result<T> {
return { success: true, data };
},
/**
* 构造失败结果。`error` 接受任意 thrown 值,内部用 `toError` 归一化为 `Error`。
*/
err<T = never>(error: unknown): Result<T> {
return { success: false, error: toError(error) };
},
/** 类型守卫:true 分支。 */
isOk<T>(r: Result<T>): r is { readonly success: true; readonly data: T } {
return r.success;
},
/** 类型守卫:false 分支。 */
isErr<T>(r: Result<T>): r is { readonly success: false; readonly error: Error } {
return !r.success;
},
/** 不可变的链式 map:失败时原样传递。 */
map<T, U>(r: Result<T>, fn: (v: T) => U): Result<U> {
return r.success ? Result.ok(fn(r.data)) : r;
},
/**
* 捕获异步 thunk 的异常并包装成 `Result<T>`。仓库内主要用这个把
* `*Api` 调用从「throw 风格」桥接到「Result 风格」。
*/
async wrap<T>(thunk: () => Promise<T>): Promise<Result<T>> {
try {
return Result.ok(await thunk());
} catch (e) {
return Result.err(e);
}
},
} as const;
/**
* 把任意 thrown 值规范化为 `Error`
* - 已是 `Error` 子类 → 原样返回(保留 `.code` / `.status` 等自定义字段)
* - string → `new Error(str)`
* - 其他 → `new Error(JSON.stringify(value))`,失败则 `new Error(String(value))`
*/
export function toError(value: unknown): Error {
if (value instanceof Error) return value;
if (typeof value === "string") return new Error(value);
try {
return new Error(JSON.stringify(value));
} catch {
return new Error(String(value));
}
}