diff --git a/.changeset/slimy-trees-notice.md b/.changeset/slimy-trees-notice.md new file mode 100644 index 0000000..a5c5bde --- /dev/null +++ b/.changeset/slimy-trees-notice.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/optic": patch +--- + +Optic: add nonNullable diff --git a/README.md b/README.md index cd41012..f8fe0ab 100644 --- a/README.md +++ b/README.md @@ -42,11 +42,9 @@ flowchart TD Let's say we have an employee and we need to upper case the first character of his company street name. ```ts -import * as O from "@fp-ts/data/Option"; - interface Street { num: number; - name: O.Option; + name: string | null; } interface Address { city: string; @@ -69,7 +67,7 @@ const from: Employee = { city: "london", street: { num: 23, - name: O.some("high street"), + name: "high street", }, }, }, @@ -83,7 +81,7 @@ const to: Employee = { city: "london", street: { num: 23, - name: O.some("High street"), + name: "High street", }, }, }, @@ -94,16 +92,15 @@ Let's see what could we do with `@fp-ts/optic` ```ts import * as Optic from "@fp-ts/optic"; -import * as OptionOptic from "@fp-ts/optic/data/Option"; import * as StringOptic from "@fp-ts/optic/data/string"; const _name: Optic.Optional = Optic.id() - .at("company") // Lens - .at("address") // Lens - .at("street") // Lens< - .at("name") // Lens> - .compose(OptionOptic.some()) // Prism, string> - .compose(StringOptic.index(0)); // Optional + .at("company") + .at("address") + .at("street") + .at("name") + .nonNullable() + .compose(StringOptic.index(0)); const capitalize = (s: string): string => s.toUpperCase(); diff --git a/src/data/Option.ts b/src/data/Option.ts index 8504128..8cfe6f6 100644 --- a/src/data/Option.ts +++ b/src/data/Option.ts @@ -18,7 +18,7 @@ export const none = (): Prism, void> => () => E.right(undefined), (a) => E.left(Error(`some(${a}) did not satisfy isNone`)) ), - (_): Option => O.none + (): Option => O.none ) /** @@ -35,5 +35,5 @@ export const some: { () => E.left([Error("none did not satisfy isSome"), O.none]), (a) => E.right(a) ), - (b) => O.some(b) + O.some ) diff --git a/src/index.ts b/src/index.ts index 6debccf..b19b606 100644 --- a/src/index.ts +++ b/src/index.ts @@ -94,6 +94,9 @@ export interface Optic< key: Key ): Optional + nonNullable(this: Prism): Prism> + nonNullable(this: Optional): Optional> + get(this: PolyLens, s: S): A getOption(this: Getter, s: S): Option @@ -140,6 +143,10 @@ class Builder< return this.compose(at(key)) } + nonNullable(): any { + return this.compose(nonNullable()) + } + get(this: PolyLens, s: S): A { return pipe(this.getOptic(s), E.getOrThrow(identity)) } @@ -433,17 +440,6 @@ export const cons: { ([head, tail]): ReadonlyArray => [head, ...tail] ) -/** - * An optic that accesses the `NonNullable` case of a nullable type. - * - * @since 1.0.0 - */ -export const nonNullable = (): Prism> => - prism( - (s) => s == null ? E.left(new Error(`${s} did not satisfy isNonNullable`)) : E.right(s as any), - identity - ) - /** * An optic that accesses the case specified by a predicate. * @@ -454,11 +450,19 @@ export const filter: { (predicate: Predicate): Prism } = (predicate: Predicate): Prism => prism( - (s) => - predicate(s) ? E.right(s) : E.left(new Error(`${s} did not satisfy the specified predicate`)), + (s) => predicate(s) ? E.right(s) : E.left(new Error(`${s} did not satisfy ${predicate.name}`)), identity ) +const isNonNullable = (s: S): s is NonNullable => s != null + +/** + * An optic that accesses the `NonNullable` case of a nullable type. + * + * @since 1.0.0 + */ +export const nonNullable = (): Prism> => filter(isNonNullable) + /** * @since 1.0.0 */ diff --git a/test/examples.ts b/test/examples.ts index db3b2bf..0cc2317 100644 --- a/test/examples.ts +++ b/test/examples.ts @@ -1,13 +1,11 @@ -import * as O from "@fp-ts/data/Option" import * as Optic from "@fp-ts/optic" -import * as OptionOptic from "@fp-ts/optic/data/Option" import * as StringOptic from "@fp-ts/optic/data/String" describe("examples", () => { it("README", () => { interface Street { num: number - name: O.Option + name: string | null } interface Address { city: string @@ -30,7 +28,7 @@ describe("examples", () => { city: "london", street: { num: 23, - name: O.some("high street") + name: "high street" } } } @@ -44,19 +42,19 @@ describe("examples", () => { city: "london", street: { num: 23, - name: O.some("High street") + name: "High street" } } } } const _name: Optic.Optional = Optic.id() - .at("company") // Lens - .at("address") // Lens - .at("street") // Lens< - .at("name") // Lens> - .compose(OptionOptic.some()) // Prism, string> - .compose(StringOptic.index(0)) // Optional + .at("company") + .at("address") + .at("street") + .at("name") + .nonNullable() + .compose(StringOptic.index(0)) const capitalize = (s: string): string => s.toUpperCase()