Skip to content
This repository has been archived by the owner on Jan 6, 2025. It is now read-only.

Commit

Permalink
add Class to /Schema module (#372)
Browse files Browse the repository at this point in the history
Co-authored-by: Tim <[email protected]>
  • Loading branch information
fubhy and tim-smart authored Aug 9, 2023
1 parent 76664ca commit 9c30196
Show file tree
Hide file tree
Showing 5 changed files with 505 additions and 36 deletions.
5 changes: 5 additions & 0 deletions .changeset/wicked-chefs-hope.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@effect/schema": patch
---

Added `Class` to `Schema` module
87 changes: 87 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,93 @@ encode({ a: O.none() }) // {}
encode({ a: O.some(1) }) // { a: 1 }
```

## Classes

As an alternative to the `struct` constructor, you can create schemas as classes by extending the `Class` utility.

They offer a few conveniences that can help with some common use cases:

- define a schema and an opaque type in one pass
- attach common functionality using class methods or getters
- check equality by value and hashing (`Class` implements `Data.Case`)

Take a look at the following example:

```ts
import * as S from "@effect/schema/Schema";

// Define your schema by extending `Class` with the desired fields
class Person extends S.Class({
id: S.number,
name: S.string
}) {
// Add getters and methods
get upperName() {
return this.name.toUpperCase();
}
}

// Extend an existing schema `Class` using the `extend` utility
class PersonWithAge extends Person.extend({
age: S.number
}) {
get isAdult() {
return this.age >= 18;
}
}

// You can use the class constructor to validate and then create a new instance from some properties
const tim = new Person({ id: 1, name: "Tim" });

// $ExpectType Schema<{ readonly id: number; name: string; }, Person>
Person.schema();

// $ExpectType Schema<{ readonly id: number; name: string; }, { readonly id: number; name: string; }>
Person.schemaStruct();
```

#### Transforms

You can enhance a class with (effectful) transforms. This can be useful if you want to embellish or validate an entity from a data store.

```ts
import * as Effect from "@effect/io/Effect";
import * as S from "@effect/schema/Schema";
import * as O from "@effect/data/Option";
import * as PR from "@effect/schema/ParseResult";

class Person extends S.Class({
id: S.number,
name: S.string
})

function fetchThing(id: number): Effect.Effect<never, Error, string> { ... }

class PersonWithTransform extends Person.transform(
{
thing: S.optional(S.string).toOption(),
},
(input) =>
Effect.mapBoth(fetchThing(input.id), {
onFailure: (e) => PR.parseError([PR.type(S.string, input, e.message)]),
onSuccess: (thing) => ({ ...input, thing: O.some(thing) })
}),
PR.success
) {}

class PersonWithTransformFrom extends Person.transformFrom(
{
thing: S.optional(S.string).toOption(),
},
(input) =>
Effect.mapBoth(fetchThing(input.id), {
onFailure: (e) => PR.parseError([PR.type(S.string, input, e.message)]),
onSuccess: (thing) => ({ ...input, thing })
}),
PR.success
) {}
```
## Pick
```ts
Expand Down
118 changes: 97 additions & 21 deletions docs/modules/Schema.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ Added in v1.0.0
- [positiveBigint](#positivebigint)
- [boolean](#boolean)
- [not](#not)
- [classes](#classes)
- [Class](#class)
- [Class (interface)](#class-interface)
- [combinators](#combinators)
- [annotations](#annotations-1)
- [array](#array-1)
Expand Down Expand Up @@ -211,11 +214,14 @@ Added in v1.0.0
- [ValidDateTypeId](#validdatetypeid)
- [utils](#utils)
- [FromOptionalKeys (type alias)](#fromoptionalkeys-type-alias)
- [FromStruct (type alias)](#fromstruct-type-alias)
- [Join (type alias)](#join-type-alias)
- [PropertySignature (interface)](#propertysignature-interface)
- [Spread (type alias)](#spread-type-alias)
- [StructFields (type alias)](#structfields-type-alias)
- [ToAsserts](#toasserts)
- [ToOptionalKeys (type alias)](#tooptionalkeys-type-alias)
- [ToStruct (type alias)](#tostruct-type-alias)
- [from](#from)
- [optional](#optional)
- [to](#to)
Expand Down Expand Up @@ -569,6 +575,55 @@ export declare const not: <I>(self: Schema<I, boolean>) => Schema<I, boolean>
Added in v1.0.0
# classes
## Class
**Signature**
```ts
export declare const Class: <Fields extends StructFields>(
fields: Fields
) => Class<Spread<FromStruct<Fields>>, Spread<ToStruct<Fields>>, {}>
```
Added in v1.0.0
## Class (interface)
**Signature**
```ts
export interface Class<I, A, Inherited = {}> {
new (props: A): A & D.Case & Omit<Inherited, keyof A>

schema<T extends new (...args: any) => any>(this: T): Schema<I, InstanceType<T>>
schemaStruct(): Schema<I, A>
extend<T extends new (...args: any) => any, Fields extends StructFields>(
this: T,
fields: Fields
): Class<
Spread<Omit<Class.From<T>, keyof Fields> & FromStruct<Fields>>,
Spread<Omit<Class.To<T>, keyof Fields> & ToStruct<Fields>>,
InstanceType<T>
>
transform<T extends new (...args: any) => any, Fields extends StructFields>(
this: T,
fields: Fields,
decode: (input: Class.To<T>) => ParseResult<Omit<Class.To<T>, keyof Fields> & ToStruct<Fields>>,
encode: (input: Omit<Class.To<T>, keyof Fields> & ToStruct<Fields>) => ParseResult<Class.To<T>>
): Class<Class.From<T>, Spread<Omit<Class.To<T>, keyof Fields> & ToStruct<Fields>>, InstanceType<T>>
transformFrom<T extends new (...args: any) => any, Fields extends StructFields>(
this: T,
fields: Fields,
decode: (input: Class.From<T>) => ParseResult<Omit<Class.From<T>, keyof Fields> & FromStruct<Fields>>,
encode: (input: Omit<Class.From<T>, keyof Fields> & FromStruct<Fields>) => ParseResult<Class.From<T>>
): Class<Class.From<T>, Spread<Omit<Class.To<T>, keyof Fields> & ToStruct<Fields>>, InstanceType<T>>
}
```

Added in v1.0.0

# combinators

## annotations
Expand Down Expand Up @@ -989,28 +1044,9 @@ Added in v1.0.0
**Signature**

```ts
export declare const struct: <
Fields extends Record<
PropertyKey,
| Schema<any, any>
| Schema<never, never>
| PropertySignature<any, boolean, any, boolean>
| PropertySignature<never, boolean, never, boolean>
>
>(
export declare const struct: <Fields extends StructFields>(
fields: Fields
) => Schema<
Spread<
{ readonly [K in Exclude<keyof Fields, FromOptionalKeys<Fields>>]: From<Fields[K]> } & {
readonly [K in FromOptionalKeys<Fields>]?: From<Fields[K]> | undefined
}
>,
Spread<
{ readonly [K in Exclude<keyof Fields, ToOptionalKeys<Fields>>]: To<Fields[K]> } & {
readonly [K in ToOptionalKeys<Fields>]?: To<Fields[K]> | undefined
}
>
>
) => Schema<Spread<FromStruct<Fields>>, Spread<ToStruct<Fields>>>
```

Added in v1.0.0
Expand Down Expand Up @@ -2463,6 +2499,18 @@ export type FromOptionalKeys<Fields> = {

Added in v1.0.0

## FromStruct (type alias)

**Signature**

```ts
export type FromStruct<Fields extends StructFields> = {
readonly [K in Exclude<keyof Fields, FromOptionalKeys<Fields>>]: From<Fields[K]>
} & { readonly [K in FromOptionalKeys<Fields>]?: From<Fields[K]> }
```

Added in v1.0.0

## Join (type alias)

**Signature**
Expand Down Expand Up @@ -2507,6 +2555,22 @@ export type Spread<A> = {

Added in v1.0.0

## StructFields (type alias)

**Signature**

```ts
export type StructFields = Record<
PropertyKey,
| Schema<any>
| Schema<never>
| PropertySignature<any, boolean, any, boolean>
| PropertySignature<never, boolean, never, boolean>
>
```

Added in v1.0.0

## ToAsserts

**Signature**
Expand All @@ -2533,6 +2597,18 @@ export type ToOptionalKeys<Fields> = {

Added in v1.0.0

## ToStruct (type alias)

**Signature**

```ts
export type ToStruct<Fields extends StructFields> = {
readonly [K in Exclude<keyof Fields, ToOptionalKeys<Fields>>]: To<Fields[K]>
} & { readonly [K in ToOptionalKeys<Fields>]?: To<Fields[K]> }
```

Added in v1.0.0

## from

**Signature**
Expand Down
Loading

0 comments on commit 9c30196

Please sign in to comment.