Skip to content

Commit

Permalink
The minItems filter now checks for an invalid argument (n < 1) an… (
Browse files Browse the repository at this point in the history
  • Loading branch information
gcanti authored Jun 9, 2024
1 parent 5ece97a commit d79ca17
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 12 deletions.
5 changes: 5 additions & 0 deletions .changeset/unlucky-flowers-suffer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@effect/schema": patch
---

The `minItems` filter now checks for an invalid argument (`n < 1`) and, when valid, refines the type to `NonEmptyReadonlyArray<A>`.
13 changes: 13 additions & 0 deletions packages/schema/dtslint/Schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2547,3 +2547,16 @@ S.asSchema(

// $ExpectType Struct<{ a: PropertySignature<"?:", string, never, ":", string, false, "a">; }>
S.Struct({ a: S.requiredToOptional(aContext, S.String, { decode: Option.some, encode: Option.getOrElse(() => "") }) })

// ---------------------------------------------
// minItems
// ---------------------------------------------

// $ExpectType Schema<readonly [string, ...string[]], readonly string[], never>
S.asSchema(S.Array(S.String).pipe(S.minItems(1)))

// $ExpectType refine<readonly [string, ...string[]], Schema<readonly string[], readonly string[], never>>
S.Array(S.String).pipe(S.minItems(1))

// $ExpectType Schema<readonly string[], readonly string[], never>
S.Array(S.String).pipe(S.minItems(1)).from
34 changes: 24 additions & 10 deletions packages/schema/src/Schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3131,6 +3131,10 @@ export function filter<C extends A, B extends A, A = C>(
refinement: (a: A, options: ParseOptions, self: AST.Refinement) => a is B,
annotations?: Annotations.Filter<C & B, C>
): <I, R>(self: Schema<C, I, R>) => refine<C & B, Schema<A, I, R>>
export function filter<A, B extends A>(
refinement: (a: A, options: ParseOptions, self: AST.Refinement) => a is B,
annotations?: Annotations.Filter<B, A>
): <I, R>(self: Schema<A, I, R>) => refine<B, Schema<A, I, R>>
export function filter<S extends Schema.Any>(
predicate: (
a: Types.NoInfer<Schema.Type<S>>,
Expand Down Expand Up @@ -5440,18 +5444,28 @@ export type MinItemsTypeId = typeof MinItemsTypeId
*/
export const minItems = <A>(
n: number,
annotations?: Annotations.Filter<ReadonlyArray<A>>
annotations?: Annotations.Filter<array_.NonEmptyReadonlyArray<A>, ReadonlyArray<A>>
) =>
<I, R>(self: Schema<ReadonlyArray<A>, I, R>): filter<Schema<ReadonlyArray<A>, I, R>> =>
self.pipe(
filter((a): a is ReadonlyArray<A> => a.length >= n, {
typeId: MinItemsTypeId,
description: `an array of at least ${n} items`,
jsonSchema: { minItems: n },
[AST.StableFilterAnnotationId]: true,
...annotations
})
<I, R>(
self: Schema<ReadonlyArray<A>, I, R>
): refine<array_.NonEmptyReadonlyArray<A>, Schema<ReadonlyArray<A>, I, R>> => {
const minItems = Math.floor(n)
if (minItems < 1) {
throw new Error(`minItems: Expected an integer greater than or equal to 1, actual ${n}`)
}
return self.pipe(
filter(
(a): a is array_.NonEmptyReadonlyArray<A> => a.length >= minItems,
{
typeId: MinItemsTypeId,
description: `an array of at least ${minItems} items`,
jsonSchema: { minItems },
[AST.StableFilterAnnotationId]: true,
...annotations
}
)
)
}

/**
* @category type id
Expand Down
10 changes: 8 additions & 2 deletions packages/schema/test/Schema/Array/minItems.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import * as S from "@effect/schema/Schema"
import * as Util from "@effect/schema/test/TestUtils"
import { describe, it } from "vitest"
import { describe, expect, it } from "vitest"

describe("minItems", () => {
const schema = S.Array(S.Number).pipe(S.minItems(2))
it("should throw for invalid argument", () => {
expect(() => S.Array(S.Number).pipe(S.minItems(-1))).toThrowError(
new Error("minItems: Expected an integer greater than or equal to 1, actual -1")
)
})

it("decoding", async () => {
const schema = S.Array(S.Number).pipe(S.minItems(2))
await Util.expectDecodeUnknownFailure(
schema,
[1],
Expand Down

0 comments on commit d79ca17

Please sign in to comment.