From 8b3cc9605a257b82b952e000a89c51ef29253876 Mon Sep 17 00:00:00 2001 From: Giulio Canti Date: Sun, 28 Jan 2024 11:04:26 +0100 Subject: [PATCH] =?UTF-8?q?Schema:=20add=20Understanding=20Schema=20Declar?= =?UTF-8?q?ation=20for=20New=20Data=20Types=20>=20Add=E2=80=A6=20(#1996)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/schema/README.md | 80 +++++++++++++++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 7 deletions(-) diff --git a/packages/schema/README.md b/packages/schema/README.md index 72b641a10e4..24341601c50 100644 --- a/packages/schema/README.md +++ b/packages/schema/README.md @@ -3078,7 +3078,9 @@ import * as S from "@effect/schema/Schema"; const isFile = (input: unknown): input is File => input instanceof File; // const FileFromSelf: S.Schema -const FileFromSelf = S.declare(isFile).pipe(S.identifier("FileFromSelf")); +const FileFromSelf = S.declare(isFile, { + identifier: "FileFromSelf", +}); const parseSync = S.decodeUnknownSync(FileFromSelf); @@ -3087,8 +3089,7 @@ console.log(parseSync(new File([], ""))); // File { size: 0, type: '', name: '', parseSync(null); /* throws -Error: FileFromSelf -└─ Expected FileFromSelf, actual null +Error: Error: Expected FileFromSelf, actual null */ ``` @@ -3097,7 +3098,6 @@ Error: FileFromSelf Type constructors are generic types that take one or more types as arguments and return a new type. If you need to define a schema for a type constructor, you can use the `S.declare` constructor. Let's illustrate this with a schema for `ReadonlySet`: ```ts -import * as Format from "@effect/schema/Format"; import * as ParseResult from "@effect/schema/ParseResult"; import * as S from "@effect/schema/Schema"; @@ -3133,8 +3133,11 @@ export const myReadonlySet = ( return ParseResult.map(elements, (is): ReadonlySet => new Set(is)); } return ParseResult.fail(ParseResult.type(ast, input)); + }, + { + description: `ReadonlySet<${S.format(item)}>`, } - ).pipe(S.description(`ReadonlySet<${Format.format(item)}>`)); + ); // const setOfNumbers: S.Schema, ReadonlySet> const setOfNumbers = myReadonlySet(S.NumberFromString); @@ -3146,8 +3149,7 @@ console.log(parseSync(new Set(["1", "2", "3"]))); // Set(3) { 1, 2, 3 } parseSync(null); /* throws -Error: ReadonlySet -└─ Expected ReadonlySet, actual null +Error: Expected ReadonlySet, actual null */ parseSync(new Set(["1", null, "3"])); @@ -3162,6 +3164,70 @@ Error: ReadonlySet */ ``` +## Adding Annotations + +When you define a new data type, some compilers like `Arbitrary` or `Pretty` may not know how to handle the newly defined data. For instance: + +```ts +import * as Arbitrary from "@effect/schema/Arbitrary"; +import * as S from "@effect/schema/Schema"; + +const isFile = (input: unknown): input is File => input instanceof File; + +const FileFromSelf = S.declare(isFile, { + identifier: "FileFromSelf", +}); + +// Create an Arbitrary instance for FileFromSelf schema +const arb = Arbitrary.make(FileFromSelf); +/* +throws: +Error: cannot build an Arbitrary for a declaration without annotations (FileFromSelf) +*/ +``` + +In such cases, you need to provide annotations to ensure proper functionality: + +```ts +import * as Arbitrary from "@effect/schema/Arbitrary"; +import * as Pretty from "@effect/schema/Pretty"; +import * as S from "@effect/schema/Schema"; +import * as fc from "fast-check"; + +const isFile = (input: unknown): input is File => input instanceof File; + +const FileFromSelf = S.declare(isFile, { + identifier: "FileFromSelf", + // Provide an arbitrary function to generate random File instances + arbitrary: () => (fc) => + fc + .tuple(fc.string(), fc.string()) + .map(([path, content]) => new File([content], path)), + // Provide a pretty function to generate human-readable representation of File instances + pretty: () => (file) => `File(${file.name})`, +}); + +// Create an Arbitrary instance for FileFromSelf schema +const arb = Arbitrary.make(FileFromSelf); + +// Generate sample files using the Arbitrary instance +const files = fc.sample(arb(fc), 2); +console.log(files); +/* +Output: +[ + File { size: 5, type: '', name: 'C', lastModified: 1706435571176 }, + File { size: 1, type: '', name: '98Ggmc', lastModified: 1706435571176 } +] +*/ + +// Create a Pretty instance for FileFromSelf schema +const pretty = Pretty.make(FileFromSelf); + +// Print human-readable representation of a file +console.log(pretty(files[0])); // "File(C)" +``` + # Useful Examples ## Email