
关于
fp-ts 函数式编程的实用指南,无学术术语,采用 80/20 法则快速出成果。适用于使用 fp-ts 库编写 TypeScript 的场景。
name: fp-ts-pragmatic description: "fp-ts 函数式编程的实用、无术语指南——用 80/20 法则获得成果,无需学术开销。在使用 fp-ts 库编写 TypeScript 时使用。" risk: safe source: "https://github.com/whatiskadudoing/fp-ts-skills" date_added: "2026-02-27"
务实的函数式编程
先读这个。 本指南穿透学术术语,展示真正重要的内容。没有范畴论。没有抽象废话。只有让你代码更好的模式。
何时使用本技能
- 刚开始使用 fp-ts 需要实用指导时
- 编写处理可空值、错误或异步操作的 TypeScript 代码时
- 想要更简洁、更可维护的函数式代码而无需学术开销时
- 将命令式代码重构为函数式风格时
黄金法则
如果函数式编程让你的代码更难读,就不要用它。
FP 是工具,不是信仰。有帮助时用它。没帮助时跳过。
FP 的 80/20 法则
这五个模式给你大部分好处。在探索其他内容之前先掌握这些。
1. Pipe:清晰地链接操作
不要嵌套函数调用或创建中间变量,按阅读顺序链接操作。
import { pipe } from 'fp-ts/function'
// 之前:难以阅读(由内向外)
const result = format(validate(parse(input)))
// 之前:太多变量
const parsed = parse(input)
const validated = validate(parsed)
const result = format(validated)
// 之后:清晰的线性流程
const result = pipe(
input,
parse,
validate,
format
)
何时使用 pipe:
- 对同一数据进行 3 个以上转换
- 发现自己在命名一次性变量
- 逻辑从上到下读更好
何时跳过 pipe:
- 只有 1-2 个操作(直接调用即可)
- 操作不能自然链接
2. Option:无需 null 检查处理缺失值
停止到处写 if (x !== null && x !== undefined)。
import * as O from 'fp-ts/Option'
import { pipe } from 'fp-ts/function'
// 之前:防御性 null 检查
function getUserCity(user: User | null): string {
if (user === null) return 'Unknown'
if (user.address === null) return 'Unknown'
if (user.address.city === null) return 'Unknown'
return user.address.city
}
// 之后:链式处理可能缺失的值
const getUserCity = (user: User | null): string =>
pipe(
O.fromNullable(user),
O.flatMap(u => O.fromNullable(u.address)),
O.flatMap(a => O.fromNullable(a.city)),
O.getOrElse(() => 'Unknown')
)
通俗翻译:
O.fromNullable(x)= "包装这个值,将 null/undefined 视为'无'"O.flatMap(fn)= "如果有值,应用这个函数"O.getOrElse(() => default)= "解包,如果为空则使用默认值"
3. Either:让错误显式化
停止为预期的失败抛出异常。将错误作为值返回。
import * as E from 'fp-ts/Either'
import { pipe } from 'fp-ts/function'
// 之前:隐藏的失败模式
function parseAge(input: string): number {
const age = parseInt(input, 10)
if (isNaN(age)) throw new Error('Invalid age')
if (age < 0) throw new Error('Age cannot be negative')
return age
}
// 之后:错误在类型中可见
function parseAge(input: string): E.Either<string, number> {
const age = parseInt(input, 10)
if (isNaN(age)) return E.left('Invalid age')
if (age < 0) return E.left('Age cannot be negative')
return E.right(age)
}
// 使用它
const result = parseAge(userInput)
if (E.isRight(result)) {
console.log(`Age is ${result.right}`)
} else {
console.log(`Error: ${result.left}`)
}
通俗翻译:
E.right(value)= "成功,带有此值"E.left(error)= "失败,带有此错误"E.isRight(x)= "成功了吗?"
4. Map:无需解包即可转换
在容器内转换值,无需先提取。
import * as O from 'fp-ts/Option'
import * as E from 'fp-ts/Either'
import * as A from 'fp-ts/Array'
import { pipe } from 'fp-ts/function'
// 在 Option 内转换
const maybeUser: O.Option<User> = O.some({ name: 'Alice', age: 30 })
const maybeName: O.Option<string> = pipe(
maybeUser,
O.map(user => user.name)
)
// 在 Either 内转换
const result: E.Either<Error, number> = E.right(5)
const doubled: E.Either<Error, number> = pipe(
result,
E.map(n => n * 2)
)
// 转换数组(同样的概念!)
const numbers = [1, 2, 3]
const doubled = pipe(
numbers,
A.map(n => n * 2)
)
5. FlatMap:链接可能失败的操作
当每一步都可能失败时,将它们链接在一起。
import * as E from 'fp-ts/Either'
import { pipe } from 'fp-ts/function'
const parseJSON = (s: string): E.Either<string, unknown> =>
E.tryCatch(() => JSON.parse(s), () => 'Invalid JSON')
const extractEmail = (data: unknown): E.Either<string, string>
兼容工具
Claude CodeCursor
标签
AI与机器学习