diff --git a/.changeset/spotty-gorillas-cry.md b/.changeset/spotty-gorillas-cry.md new file mode 100644 index 000000000..253603bed --- /dev/null +++ b/.changeset/spotty-gorillas-cry.md @@ -0,0 +1,5 @@ +--- +"@effect/schema": patch +--- + +add TaggedRequest class diff --git a/docs/modules/Schema.ts.md b/docs/modules/Schema.ts.md index 40e016603..89b9ce151 100644 --- a/docs/modules/Schema.ts.md +++ b/docs/modules/Schema.ts.md @@ -94,6 +94,10 @@ Added in v1.0.0 - [Class (interface)](#class-interface) - [TaggedClass](#taggedclass) - [TaggedError](#taggederror) + - [TaggedRequest](#taggedrequest) + - [TaggedRequest (namespace)](#taggedrequest-namespace) + - [Base (interface)](#base-interface) + - [ResultSchemas (interface)](#resultschemas-interface) - [combinators](#combinators) - [array](#array) - [attachPropertySignature](#attachpropertysignature) @@ -1159,6 +1163,67 @@ export declare const TaggedError: () => () => ( + tag: Tag, + failure: Schema, + success: Schema, + fields: Fields +) => [unknown] extends [Self] + ? 'Missing `Self` generic - use `class Self extends TaggedRequest()("Tag", SuccessSchema, FailureSchema, { ... })`' + : Class< + Simplify< + { readonly _tag: Tag } & { + readonly [K in Exclude>]: Schema.From + } & { readonly [K in FromOptionalKeys]?: Schema.From | undefined } + >, + Simplify< + { readonly _tag: Tag } & { + readonly [K in Exclude>]: Schema.To + } & { readonly [K in ToOptionalKeys]?: Schema.To | undefined } + >, + Simplify>, + Self, + Request.Request + > & + TaggedRequest.ResultSchemas +``` + +Added in v1.0.0 + +## TaggedRequest (namespace) + +Added in v1.0.0 + +### Base (interface) + +**Signature** + +```ts +export interface Base> + extends Schema, + TaggedRequest.ResultSchemas {} +``` + +Added in v1.0.0 + +### ResultSchemas (interface) + +**Signature** + +```ts +export interface ResultSchemas { + readonly Failure: Schema + readonly Success: Schema +} +``` + +Added in v1.0.0 + # combinators ## array diff --git a/src/Schema.ts b/src/Schema.ts index 71e80f1d9..43c44589f 100644 --- a/src/Schema.ts +++ b/src/Schema.ts @@ -19,6 +19,7 @@ import type { Pipeable } from "effect/Pipeable" import { pipeArguments } from "effect/Pipeable" import * as Predicate from "effect/Predicate" import * as ReadonlyArray from "effect/ReadonlyArray" +import * as Request from "effect/Request" import * as S from "effect/String" import type { Equals, Simplify } from "effect/Types" import type { Arbitrary } from "./Arbitrary.js" @@ -3691,6 +3692,64 @@ export const TaggedError = () => ) } +/** + * @category classes + * @since 1.0.0 + */ +export declare namespace TaggedRequest { + /** + * @category classes + * @since 1.0.0 + */ + export interface Base> + extends Schema, TaggedRequest.ResultSchemas + {} + + /** + * @category classes + * @since 1.0.0 + */ + export interface ResultSchemas { + readonly Failure: Schema + readonly Success: Schema + } +} + +/** + * @category classes + * @since 1.0.0 + */ +export const TaggedRequest = + () => + ( + tag: Tag, + failure: Schema, + success: Schema, + fields: Fields + ): [unknown] extends [Self] ? + MissingSelfGeneric<"TaggedRequest", `"Tag", SuccessSchema, FailureSchema, `> + : + & Class< + Simplify<{ readonly _tag: Tag } & FromStruct>, + Simplify<{ readonly _tag: Tag } & ToStruct>, + Simplify>, + Self, + Request.Request + > + & TaggedRequest.ResultSchemas => + { + const fieldsWithTag = { ...fields, _tag: literal(tag) } + const Base = makeClass( + struct(fieldsWithTag), + fieldsWithTag, + Request.Class, + { _tag: tag } + ) + Base.Failure = failure + Base.Success = success + return Base + } + const makeClass = ( selfSchema: Schema, selfFields: StructFields, diff --git a/test/Schema/Class.test.ts b/test/Schema/Class.test.ts index 23485c339..302967a00 100644 --- a/test/Schema/Class.test.ts +++ b/test/Schema/Class.test.ts @@ -6,6 +6,7 @@ import { Effect } from "effect" import * as Data from "effect/Data" import * as Equal from "effect/Equal" import * as O from "effect/Option" +import * as Request from "effect/Request" import { describe, expect, it } from "vitest" class Person extends S.Class()({ @@ -237,4 +238,35 @@ describe("Schema/Class", () => { expect(err._tag).toEqual("MyError") expect(err.id).toEqual(1) }) + + it("TaggedRequest", () => { + class MyRequest extends S.TaggedRequest()("MyRequest", S.string, S.number, { + id: S.number + }) {} + + let req = new MyRequest({ id: 1 }) + expect(req._tag).toEqual("MyRequest") + expect(req.id).toEqual(1) + expect(Request.isRequest(req)).toEqual(true) + + req = S.decodeSync(MyRequest)({ _tag: "MyRequest", id: 1 }) + expect(req._tag).toEqual("MyRequest") + expect(req.id).toEqual(1) + expect(Request.isRequest(req)).toEqual(true) + + S.decodeSync(MyRequest.Success)(123) + S.decodeSync(MyRequest.Failure)("fail") + }) + + it("TaggedRequest assignable to TaggedRequest.Base", () => { + class MyRequest extends S.TaggedRequest()("MyRequest", S.string, S.number, { + id: S.number + }) {} + + const makeCache = >( + schema: S.TaggedRequest.Base + ) => schema + + makeCache(MyRequest) + }) })