@fp-ts/optic

中文文档
0.25.0发布于 2024/09/20MITTypeScriptESM + CJS零依赖近 30 天下载 8078
中文使用文档
文档目录18 节
  1. 导言
  2. 所需资源
  3. 开始
  4. 镜头
  5. 棱镜
  6. 其它
  7. 透视构造器
  8. Getter / Stetter APIs 图像
  9. 编号
  10. 编曲
  11. 时间
  12. 选择
  13. 删除
  14. 过滤器
  15. 非无
  16. 有点
  17. 指数
  18. 键

移植 齐奥光学 到类型脚本

npm downloads

flowchart TD
  Iso --> Lens
  Iso --> Prism
  Lens --> ReversedPrism
  ReversedPrism --> Optional
  Prism --> Optional
  Optional --> Getter
  Optional --> Setter
  Getter --> Optic
  Setter --> Optic

特征

  • 光子的统一代表。所有光学的构成方式相同,因为它们都是同一数据类型的实例(Optic(中文(简体) )
  • 一体化。用于 effect 数据结构,例如 Option 财务报告和审定财务报表 Either。 。 。 .

导言

@fp-ts/optic 是一个库,可以方便地根据光学的单一表现来修改较大数据结构的部件,作为取出器和定出器的组合.

@fp-ts/optic 具有光学的统一代表性,深度 effect 集成, 帮助错误消息 .

信贷和担保

这个图书馆受到以下项目的启发:

非常感谢我的赞助商,他们开发了 @fp-ts/optic 也许吧.

如果你还想 成为提案人 确保这个图书馆继续改进和接受维护 GitHub 赞助者简介

所需资源

  • 类型脚本 5.4 或更新
  • 这个 strict 启用您的旗帜 tsconfig.json 文件
{
  // ...
  "compilerOptions": {
    // ...
    "strict": true
  }
}

开始

安装 阿尔法 版本 :

npm install @fp-ts/optic

3⁄4 ̄ ̧漯B。本套件的发布主要是为了接收早期反馈,对于贡献者来说,在这个开发阶段,我们不能保证APIs的稳定性,认为每个版本都包含断续续的更改.

一旦安装了库,您可以从库中导入必要的类型和功能 @fp-ts/optic 模块.

import * as Optic from "@fp-ts/optic";

内容提要

假设我们有个员工 并且我们得给他公司街名上的第一个角色.

import * as Option from "effect/Option";

interface Street {
  readonly num: number;
  readonly name: Option.Option<string>;
}
interface Address {
  readonly city: string;
  readonly street: Street;
}
interface Company {
  readonly name: string;
  readonly address: Address;
}
interface Employee {
  readonly name: string;
  readonly company: Company;
}

const from: Employee = {
  name: "john",
  company: {
    name: "awesome inc",
    address: {
      city: "london",
      street: {
        num: 23,
        name: Option.some("high street"),
      },
    },
  },
};

const to: Employee = {
  name: "john",
  company: {
    name: "awesome inc",
    address: {
      city: "london",
      street: {
        num: 23,
        name: Option.some("High street"),
      },
    },
  },
};

让我们看看我们能做什么 @fp-ts/optic

import * as Optic from "@fp-ts/optic";
import * as StringOptic from "@fp-ts/optic/String";
import * as String from "effect/String";
import * as assert from "node:assert";

const _firstChar: Optic.Optional<Employee, string> = Optic.id<Employee>()
  .at("company")
  .at("address")
  .at("street")
  .at("name")
  .some()
  .compose(StringOptic.index(0));

const capitalizeName = Optic.modify(_firstChar)(String.toUpperCase);

assert.deepStrictEqual(capitalizeName(from), to);

理解视觉

@fp-ts/optic 其基础是光学的单一表示,作为取出器和定出器的组合.

export interface Optic<
  in GetWhole,
  in SetWholeBefore,
  in SetPiece,
  out GetError,
  out SetError,
  out GetPiece,
  out SetWholeAfter,
> {
  readonly getOptic: (
    GetWhole: GetWhole
  ) => Either<readonly [GetError, SetWholeAfter], GetPiece>;
  readonly setOptic: (
    SetPiece: SetPiece
  ) => (
    SetWholeBefore: SetWholeBefore
  ) => Either<readonly [SetError, SetWholeAfter], SetWholeAfter>;
}

获取器可以使用一些更大的类型结构 GetWhole 并获得它的一部分类型 GetPiece。它可能因类型错误而失败 GetError 因为我们试图得到的部分 可能不存在 在更大的结构.

摆放器有它的能力,因为它有些类型 SetPiece 和原类型结构 SetWholeBefore,返回新的类型结构 SetWholeAfter设置会因类型错误而失败 SetError 因为我们试图设置的棋子 可能不存在的结构.

镜头

A 类 Lens 是一种访问产品类型领域的光学技术,例如tuple或struct.

这个 GetError 类型 Lens 实值 never 因为我们总能得到一个产品类型的领域。 这个 SetError 类型也是 never 因为我们总能把产品类型的领域设置到一个新的值.

在这种情况下 GetWhole, (中文(简体) ), SetWholeBefore,以及 SetWholeAfter 类型相同,并代表产品类型。 这个 GetPiece 财务报告和审定财务报表 SetPiece 类型也是相同的,并代表字段.

因此,我们:

export interface Lens<in out S, in out A>
  extends Optic<S, S, A, never, never, A, S> {}

简化的签名是:

export interface Lens<in out S, in out A> {
  readonly getOptic: (s: S) => Either<never, A>;
  readonly setOptic: (a: A) => (s: S) => Either<never, S>;
}

这完全符合我们上面的描述。 透镜是一种光学,我们总能得到更大的结构的一部分, 并且赋予一个原始的结构,我们总能在那个结构中设定一个新的价值.

棱镜

A 类 Prism 是一种光学,可以访问一个总和型的相机,例如: Left 或者说 Right 案件数量 Either。 。 。 .

用棱镜从更大的数据结构中获取一部分可能会失败,因为我们试图访问的案例可能不存在. 例如,我们可能试图进入一个 Either 但其中之一其实是 Left。 。 。 .

我们使用数据类型 Error 来模拟不同的方式 获取或设定 光学会失败。 所以说 GetError 棱镜类型 Error。 。 。 .

这个 SetError 棱镜类型 never 因为鉴于某种产品类型的一种情况,我们总能返回一种产品类型的新价值,因为产品类型的每一种情况都是产品类型的一种情况.

棱镜也不同于镜头,因为我们不需要任何原始结构来设定. 一种产品类型只包括它的案例, 所以如果我们有我们想要设定的新的案例价值, 我们可以仅仅使用这个价值, 而不需要原始的结构.

我们用 unknown 联 合 国 SetWholeBefore 类型,表示我们不需要任何原始结构来设置新值.

因此,棱镜的定义是:

export interface Prism<in out S, in out A>
  extends Optic<S, unknown, A, Error, never, A, S> {}

而简化的签名是:

export interface Prism<in out S, in out A> {
  readonly getOptic: (s: S) => Either<Error, A>;
  readonly setOptic: (a: A) => (s: unknown) => Either<never, S>;
}

这同样符合我们的描述。 棱镜是一种光学,我们也许无法获得一个值,但总是可以设定一个值,事实上不需要任何原始结构来设定.

其它

@fp-ts/optic 支持多种其他光学:

  • 可选。一个 Optional 是一种光学,它访问一个更大的结构的一部分,其中访问的部分可能不存在,而且该结构不仅包含该部分。 两者 GetError 财务报告和审定财务报表 SetError 类型是 Error 因为这个部分在结构中可能不存在,而设置中确实需要原始结构,因为它包含的不仅仅是这个部分.
  • 伊苏。一个 Iso 是一个光学的,它访问一个结构的一部分, 结构中除了部分之外,什么都不是。 两者 GetError 财务报告和审定财务报表 SetError 类型是 never 编辑 SetWholeBefore 类型是 unknown。 。 。 .
  • 盖特A级 Getter 光学只允许获得一个值。 这个 SetWholeBefore 财务报告和审定财务报表 SetPiece 类型是 never 因为它永远无法设置.
  • 编辑器A级 Setter 是一个只允许设置值的光学。 这个 GetWhole 类型是 never 因为它不可能永远得到.

每种光学的多态性版本也比较多,使得数据结构的种类和前后部分不同. 例如,a PolyPrism 可以让我们找到一个合适的案子 Either<A, B> 设置一个 C 要返回的数值 Either<A, C>。 。 。 .

计费表

透视构造器

图像名称鉴于改为:
伊苏实值S => A, (中文(简体) ), A => SIso<S, A>
编号Iso<S, S>
镜头镜头S => A, (中文(简体) ), A => S => SLens<S, A>
时间KeyLens<S, S[Key]>
选择KeyLens<S, Pick<S, Key>>
删除KeyLens<S, Omit<S, Key>>
棱镜棱镜S => Either<Error, A>, (中文(简体) ), A => SPrism<S, A>
多边棱镜S => Either<[Error, T], A>, (中文(简体) ), B => TPolyPrism<S, T, A, B>
骗局Prism<A[], [A, A[]]>
非无Prism<A, NonNullable<A>>
有点Prism<Option<A>, A>
过滤器Predicate<S>Prism<S, S>
过滤器Refinement<S, A>Prism<S, A>
可选选项S => Either<Error, A>, (中文(简体) ), A => S => Either<Error, S>Optional<S, A>
多选项S => Either<[Error, T], A>, (中文(简体) ), B => S => Either<[Error, T], T>PolyOptional<S, T, A, B>
指数numberOptional<A[], A>
键stringOptional<{ []: A }, A>
头Optional<A[], A>
尾声Optional<A[], A[]>
找到第一个Predicate<A>Optional<A[], A>
找到第一个Refinement<A, B>Optional<A[], B>

Getter / Stetter APIs 图像

名称鉴于改为:
起来Lens<S, A>, (中文(简体) ), SA
解码Prism<S, A>, (中文(简体) ), SEither<Error, A>
编码Prism<S, A>, (中文(简体) ), AS
获得 Ordify 编辑PolyOptional<S, T, A, B>, (中文(简体) ), SEither<T, A>
修改Optional<S, A>, (中文(简体) ), A => AS => S
替换Setter<S, A>, (中文(简体) ), A, (中文(简体) ), SS
替换选项Setter<S, A>, (中文(简体) ), A, (中文(简体) ), SOption<S>
获取选项Getter<S, A>, (中文(简体) ), SOption<A>

基本用途

编号

这个 id 光学是一种代表身份函数的特殊光学,它简单地返回它的输入而不变. 可以认为是光学的"基案",从中可以建造出更复杂的光学.

这个 id 光学被定义为单通类型,这意味着它只有一个可能的值. 这使得它很容易成为建造更大型光学的起点,因为它不需要任何参数或配置.

import * as Optic from "@fp-ts/optic";

interface Whole {
  readonly a: string;
  readonly b: number;
  readonly c: boolean;
}

// create an iso that focuses on the 'Whole' data structure
const _a: Optic.Iso<Whole, Whole> = Optic.id<Whole>();

编曲

这个 compose 方法是一种实用函数,可以将两个或两个以上的光学组合成一个光学.

import { pipe } from "effect";
import * as Optic from "@fp-ts/optic";

// This is the type of the data structure that the lens will be operating on.
interface Whole {
  readonly a: string;
  readonly b: number;
  readonly c: boolean;
}

// This creates a lens that focuses on the 'a' field within the 'Whole' object.
const _a: Optic.Lens<Whole, string> =
  // The 'id' function creates an identity lens for the 'Whole' type.
  Optic.id<Whole>()
    // The 'compose' method combines the identity lens with an 'at' lens,
    // which selects the 'a' field within the 'Whole' object.
    .compose(Optic.at("a"));

// Now we can use the '_a' lens to view and modify the 'a' field of a 'Whole' object.

const whole: Whole = {
  a: "foo",
  b: 42,
  c: true,
};

// Use the 'get' function to view the 'a' field of the 'whole' object.
const result: string = pipe(whole, Optic.get(_a)); // returns "foo"

// Use the 'replace' function to update the 'a' field of the 'whole' object.
const updated: Whole = pipe(whole, Optic.replace(_a)("bar")); // returns { a: "bar", b: 42, c: true }

时间

这个 at 方法是一种实用函数,它创造了一个光学功能,它侧重于数据结构内的特定字段.

import { pipe } from "effect";
import * as Optic from "@fp-ts/optic";

// This is the type of the data structure that the lens will be operating on.
interface Whole {
  readonly a: string;
  readonly b: number;
  readonly c: boolean;
}

// This creates a lens that focuses on the 'a' field within the 'Whole' object.
const _a: Optic.Lens<Whole, string> =
  // The 'id' function creates an identity lens for the 'Whole' type.
  Optic.id<Whole>()
    // The 'at' method selects the 'a' key within the 'Whole' object,
    // resulting in a lens that is focused on that field.
    .at("a");

// Now we can use the '_a' lens to view and modify the 'a' field of a 'Whole' object.

const whole: Whole = {
  a: "foo",
  b: 42,
  c: true,
};

// Use the 'get' function to view the 'a' field of the 'whole' object.
const result: string = pipe(whole, Optic.get(_a)); // returns "foo"

// Use the 'replace' function to update the 'a' field of the 'whole' object.
const updated: Whole = pipe(whole, Optic.replace(_a)("bar")); // returns { a: "bar", b: 42, c: true }

选择

这个 pick 方法是一种实用函数,它创建了光学,侧重于数据结构内的一组密钥.

import { pipe } from "effect";
import * as Optic from "@fp-ts/optic";

// This is the type of the data structure that the lens will be operating on.
interface Whole {
  readonly a: string;
  readonly b: number;
  readonly c: boolean;
}

// This creates a lens that focuses on the 'a' and 'b' fields within the 'Whole' object.
const _ab: Optic.Lens<Whole, { readonly a: string; readonly b: number }> =
  // The 'id' function creates an identity lens for the 'Whole' type.
  Optic.id<Whole>()
    // The 'pick' method selects the 'a' and 'b' keys within the 'Whole' object,
    // resulting in a lens that is focused on those fields.
    .pick("a", "b");

// Now we can use the '_ab' lens to view and modify the 'a' and 'b' fields of a 'Whole' object.

const whole: Whole = {
  a: "foo",
  b: 42,
  c: true,
};

// Use the 'get' function to view the 'a' and 'b' fields of the 'whole' object.
const result: { readonly a: string; readonly b: number } = pipe(
  whole,
  Optic.get(_ab)
); // returns { a: "foo", b: 42 }

// Use the 'replace' function to update the 'a' and 'b' fields of the 'whole' object.
const updated: Whole = pipe(whole, Optic.replace(_ab)({ a: "bar", b: 23 })); // returns { a: "bar", b: 23, c: true }

删除

这个 omit 方法是一个工具函数,它创建一个将一组密钥排除在结构外的透镜。 当您想要专注于一个数据结构的子集而忽略某些字段时,这可能会有用.

import { pipe } from "effect";
import * as Optic from "@fp-ts/optic";

interface Whole {
  readonly a: string;
  readonly b: number;
  readonly c: boolean;
}

// This creates a lens that focuses on the 'a' and 'c' fields within the 'Whole' object.
const _ac: Optic.Lens<Whole, { readonly a: string; readonly c: boolean }> =
  // The 'id' function creates an identity lens for the 'Whole' type.
  Optic.id<Whole>()
    // The 'omit' method excludes the 'b' key within the 'Whole' object,
    // resulting in a lens that is focused on the 'a' and 'c' fields.
    .omit("b");

// Now we can use the '_ac' lens to view and modify the 'a' and 'c' fields of a 'Whole' object.

const whole: Whole = {
  a: "foo",
  b: 42,
  c: true,
};

// Use the 'get' function to view the 'a' and 'c' fields of the 'whole' object.
const result: { readonly a: string; readonly c: boolean } = pipe(
  whole,
  Optic.get(_ac)
); // returns { a: "foo", c: true }

// Use the 'replace' function to update the 'a' and 'c' fields of the 'whole' object.
const updated: Whole = pipe(whole, Optic.replace(_ac)({ a: "bar", c: false })); // returns { a: "bar", b: 42, c: false }

过滤器

这个 filter 方法是一种实用函数,它创造了一个光学函数,它侧重于一个数据结构的元素,与指定的上游相匹配.

import { pipe } from "effect";
import * as Optic from "@fp-ts/optic";
import type { Option } from "effect/Option";

// This is the type of the data structure that the prism will be operating on.
interface Whole {
  readonly a: number;
}

// This creates an `Optional` that focuses on the 'a' field within the 'Whole' object,
// and only includes values that are even numbers.
const _evenA: Optic.Optional<Whole, number> =
  // The 'id' function creates an identity prism for the 'Whole' type.
  Optic.id<Whole>()
    // The 'at' method selects the 'a' key within the 'Whole' object,
    // resulting in a `Lens` that is focused on that field.
    .at("a")
    // The 'filter' method only includes values that are even numbers.
    .filter((a) => a % 2 === 0);

// Now we can use the '_evenA' `Optional` to view and modify the 'a' field of a 'Whole' object,
// but only if the value is an even number.

const whole: Whole = {
  a: 2,
};

// Use the 'getOption' function to view the 'a' field of the 'whole' object.
const result: Option<number> = pipe(whole, Optic.getOption(_evenA)); // returns some(2)

// Use the 'replace' function to update the 'a' field of the 'whole' object.
const updated: Whole = pipe(whole, Optic.replace(_evenA)(4)); // returns { a: 4 }

非无

这个 nonNullable 方法是一个创建 Prism ,侧重于无效类型的不可变值。 如果您想要操纵或提取无效类型的值, 但想要忽略 null 数值.

import { pipe } from "effect";
import * as Optic from "@fp-ts/optic";
import type { Option } from "effect/Option";

const _nonNullString: Optic.Prism<string | null, string> = Optic.id<
  string | null
>().nonNullable();

const result1: Option<string> = pipe("foo", Optic.getOption(_nonNullString)); // returns some("foo")
const result2: Option<string> = pipe(null, Optic.getOption(_nonNullString)); // returns none

有点

这个 some 方法是一种效用函数,可以创建一个侧重于光学的光学函数 Some 情况 Option 数据类型。 此光学允许您查看并修改其中包含的值 Some 情况 Option。 。 。 .

import { pipe } from "effect";
import * as Option from "effect/Option";
import * as Optic from "@fp-ts/optic";

// This creates a prism that focuses on the 'Some' case of the 'Option<number>' object.
const _some: Optic.Prism<Option.Option<number>, number> = Optic.id<
  Option.Option<number>
>().some();

const option: Option.Option<number> = Option.some(42);

const result: Option.Option<number> = pipe(option, Optic.getOption(_some)); // returns some(42)

const updated: Option.Option<number> = pipe(option, Optic.replace(_some)(23)); // returns some(23)

指数

这个 index 方法创建 Optional 光学,其重点是a ReadonlyArray编辑 Optional 光学允许您在指定的索引中查看值,或者 None 如果索引不存在。 你也可以使用这个 Optional 光学可以更新指定索引中的值,如果存在的话.

import { pipe } from "effect";
import * as Optic from "@fp-ts/optic";
import type { Option } from "effect/Option";

const _index2: Optic.Optional<ReadonlyArray<number>, number> = Optic.id<
  ReadonlyArray<number>
>().index(2);

const arr: ReadonlyArray<number> = [1, 2, 3, 4];

const result: Option<number> = pipe(arr, Optic.getOption(_index2)); // some(3)
const updated1: ReadonlyArray<number> = pipe(arr, Optic.replace(_index2)(42)); // [1, 2, 42, 4]
const updated2: ReadonlyArray<number> = pipe(arr, Optic.replace(_index2)(10)); // [1, 2, 10, 4]
const updated3: ReadonlyArray<number> = pipe([], Optic.replace(_index2)(10)); // []

键

这个 key 方法是允许您创建 Optional 以索引签名的特定密钥(有字符串索引签名的类型)为重点的光学.

import { pipe } from "effect";
import type { Option } from "effect/Option";
import * as Optic from "@fp-ts/optic";

interface Data {
  readonly [key: string]: number;
}

// Create an Optional optic that focuses on the 'foo' key.
const _foo: Optic.Optional<Data, number> = Optic.id<Data>().key("foo");

const data: Data = {
  foo: 1,
  bar: 2,
};

// Use the 'getOption' function to view the value of the 'foo' key.
const fooValue: Option<number> = pipe(data, Optic.getOption(_foo)); // returns some(1)

// Use the 'replace' function to update the value of the 'foo' key.
const updatedData: Data = pipe(data, Optic.replace(_foo)(10)); // returns { foo: 10, bar: 2 }

文档

许可证

《麻省理工学院许可证》

版本与发布记录

@fp-ts/optic 共发布 32 个版本,首次发布于 2022/11/25;过去 12 个月发布了 0 个版本。

版本发布日期距今
0.25.0latest2024/09/202 年前
0.24.02024/04/172 年前
0.23.02024/02/242 年前
0.22.02024/01/032 年前
0.21.02023/12/222 年前
0.20.02023/12/112 年前

关于 @fp-ts/optic 的常见问题

如何安装 @fp-ts/optic?

在项目目录运行 npm install @fp-ts/optic,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:@effect/typeclass(^0.27.0)、effect(^3.8.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@fp-ts/optic 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@fp-ts/optic 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@fp-ts/optic 可以商用吗?

@fp-ts/optic 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@fp-ts/optic 的下载量有多少?

根据 npm 官方下载统计,@fp-ts/optic 在 2026/10/16 – 2026/11/14 这 30 天内被下载 8,078 次,日均约 269 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@fp-ts/optic 还在维护吗?

最新版本 0.25.0 发布于 2024/09/20(2 年前);至今共发布 32 个版本,其中过去 12 个月发布了 0 个;首次发布于 2022/11/25。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。