0Pricing
TypeScript Academy · 课时

Result/Either 风格的类型

使用 Result/Either 联合类型明确表示成功与失败,添加辅助工具(map/flatMap/mapError),无需到处使用 try/catch 也能让流程保持可预测。

Result/Either 风格的类型 是 CoddyKit 上的免费 TypeScript Academy 课时。 这是第 1 节课,共 2 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 TypeScript Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 TypeScript Academy 课程共包含 2 节课。

简介

目标:让失败可见。您将使用判别式联合类型建模结果,使用辅助函数进行组合,并在边界处转换异常。

  • 结果类型
  • map/flatMap/mapError
  • HTTP 集成

结果核心

创建一个小型结果工具:使用 ok/err 构造函数,以及用于组合的 map、flatMap、mapError。

export type Ok<T>  = { ok: true;  value: T }
export type Err<E> = { ok: false; error: E }
export type Result<T, E> = Ok<T> | Err<E>

export const ok = <T>(value: T): Ok<T> => ({ ok: true, value })
export const err = <E>(error: E): Err<E> => ({ ok: false, error })

export function map<T, E, U>(r: Result<T, E>, f: (t: T) => U): Result<U, E> {
  return r.ok ? ok(f(r.value)) : r
}
export function flatMap<T, E, U>(r: Result<T, E>, f: (t: T) => Result<U, E>): Result<U, E> {
  return r.ok ? f(r.value) : r
}
export function mapError<T, E, F>(r: Result<T, E>, g: (e: E) => F): Result<T, F> {
  return r.ok ? r : err(g(r.error))
}

领域错误

使用带有 tag 的判别式联合类型来建模领域错误。每个分支都携带精确的数据,以便生成良好的消息。

export type CreateUserError =
  | { tag: "InvalidEmail"; detail?: string }
  | { tag: "Duplicate"; email: string }

export function validateEmail(email: string): Result<string, CreateUserError> {
  const okEmail = /.+@.+/.test(email)
  return okEmail ? ok(email.toLowerCase()) : err({ tag: "InvalidEmail", detail: "bad format" })
}

const existing = new Set(["ada@example.com"]) // pretend DB
export function saveUser(email: string): Result<{ id: string }, CreateUserError> {
  if (existing.has(email)) return err({ tag: "Duplicate", email })
  return ok({ id: Math.random().toString(36).slice(2) })
}

组合流程

使用 flatMap 组合验证。使用分支语句和 never 检查,在编译时强制确保所有情况都已处理。

export function createUser(flowEmail: string) {
  return flatMap(validateEmail(flowEmail), email => saveUser(email))
}

// Use
const r = createUser("Ada@example.com")
if (r.ok) {
  console.log("id:", r.value.id)
} else {
  switch (r.error.tag) {
    case "InvalidEmail": console.error("invalid email:", r.error.detail); break
    case "Duplicate": console.error("already used:", r.error.email); break
    default: ((x: never) => x)(r.error) // exhaustiveness
  }
}

异常 → 结果

在输入/输出边界处包装异常,并将其转换为 Result。调用方只需处理一条统一的错误路径。

export async function fromPromise<T>(p: Promise<T>): Promise<Result<T, { tag: "Exception"; message: string }>> {
  try { return ok(await p) }
  catch (e) { return err({ tag: "Exception", message: e instanceof Error ? e.message : String(e) }) }
}

// Example: wrap a fetch
export async function fetchJson<T>(url: string): Promise<Result<T, { tag: "Exception"; message: string }>> {
  return fromPromise(fetch(url).then(r => r.json() as T))
}

HTTP 映射提示

HTTP 集成:将领域错误映射为400/409;将输入/输出异常映射为502/503。保持映射函数简短,让控制器保持整洁且行为可预测。

结果类型收益检查

快速检查:为什么优先使用判别式结果联合类型?

回顾

回顾:定义一个小型结果类型,使用 map/flatMap 进行组合,将领域错误建模为联合类型,并在边界处转换异常。

常见问题解答

「Result/Either 风格的类型」课时是免费的吗?

是的 — 「Result/Either 风格的类型」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 TypeScript Academy 课程的其余内容,请升级到 CoddyKit PRO。 TypeScript Academy 课程共包含 2 节课。

「Result/Either 风格的类型」这节课中我会学到什么?

使用 Result/Either 联合类型明确表示成功与失败,添加辅助工具(map/flatMap/mapError),无需到处使用 try/catch 也能让流程保持可预测。 你通过在浏览器中直接运行的动手代码来练习 TypeScript Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 TypeScript Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 TypeScript Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 2 节。

「Result/Either 风格的类型」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 TypeScript Academy 课中编写并运行代码吗?

能。每节 TypeScript Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. Result/Either 风格的类型
  2. 使用可区分联合进行全面错误处理
← 返回 TypeScript Academy