Skip to content

Commit 317f012

Browse files
authored
feat: chainable constraints (#995)
1 parent de3867d commit 317f012

29 files changed

+685
-113
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@arktype/util": patch
3+
---
4+
5+
Add `leftIfEqual` type utility

.changeset/tender-wombats-march.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@arktype/schema": patch
3+
---
4+
5+
Fixed a cyclic traversal case (see [arktype CHANGELOG](../type/CHANGELOG.md))

ark/dark/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "arkdark",
33
"displayName": "ArkDark",
44
"description": "ArkType syntax highlighting and theme⛵",
5-
"version": "5.2.2",
5+
"version": "5.2.3",
66
"publisher": "arktypeio",
77
"type": "module",
88
"scripts": {
@@ -59,11 +59,11 @@
5959
},
6060
"errorLens.replace": [
6161
{
62-
"matcher": "^(?:Type|Argument of type) '.*' is not assignable to (?:parameter of )?type 'keyError<\"(.*)\">'\\.$",
62+
"matcher": "'keyError<\"(.*)\">'\\.$",
6363
"message": "$1"
6464
},
6565
{
66-
"matcher": "^(?:Type|Argument of type) '.*' is not assignable to (?:parameter of )?type '\"(.*\\u200A)\"'\\.$",
66+
"matcher": "'\"(.*\\u200A)\"'\\.$",
6767
"message": "$1"
6868
},
6969
{

ark/schema/api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export * from "./predicate.js"
1717
export * from "./refinements/after.js"
1818
export * from "./refinements/before.js"
1919
export * from "./refinements/divisor.js"
20+
export * from "./refinements/exactLength.js"
2021
export * from "./refinements/max.js"
2122
export * from "./refinements/maxLength.js"
2223
export * from "./refinements/min.js"

ark/schema/ast.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import type { conform } from "@arktype/util"
1+
import type { conform, leftIfEqual } from "@arktype/util"
22
import type { PrimitiveConstraintKind } from "./constraint.js"
33
import type { NodeSchema } from "./kinds.js"
44
import type { constraintKindOf } from "./roots/intersection.js"
5+
import type { MorphAst, Out } from "./roots/morph.js"
56

67
export type Comparator = "<" | "<=" | ">" | ">=" | "=="
78

@@ -126,6 +127,11 @@ export type LessThanLength<rule extends number> = {
126127
max: { [k in rule]: 0 }
127128
}
128129

130+
export type ExactlyLength<rule extends number> = {
131+
min: { [k in rule]: 1 }
132+
max: { [k in rule]: 1 }
133+
}
134+
129135
export namespace string {
130136
export type atLeastLength<rule extends number> = of<
131137
string,
@@ -144,6 +150,11 @@ export namespace string {
144150
LessThanLength<rule>
145151
>
146152

153+
export type exactlyLength<rule extends number> = of<
154+
string,
155+
ExactlyLength<rule>
156+
>
157+
147158
export type matching<rule extends string> = of<string, Matching<rule>>
148159

149160
export type narrowed = of<string, Narrowed>
@@ -164,6 +175,7 @@ export namespace string {
164175
lessThanLength<rule & number>
165176
: atMostLength<rule & number>
166177
: kind extends "regex" ? matching<rule & string>
178+
: kind extends "exactLength" ? exactlyLength<rule & number>
167179
: narrowed
168180
: never
169181
}
@@ -227,11 +239,9 @@ export type constrain<
227239
kind extends PrimitiveConstraintKind,
228240
schema extends NodeSchema<kind>
229241
> =
230-
_constrain<t, kind, schema> extends infer constrained ?
231-
[t, constrained] extends [constrained, t] ?
232-
t
233-
: constrained
234-
: never
242+
t extends MorphAst<infer i, infer o> ?
243+
(In: leftIfEqual<i, _constrain<i, kind, schema>>) => Out<o>
244+
: leftIfEqual<t, _constrain<t, kind, schema>>
235245

236246
type _constrain<
237247
t,
@@ -282,6 +292,7 @@ export type schemaToConstraint<
282292
schema extends { exclusive: true } ?
283293
LessThanLength<rule & number>
284294
: AtMostLength<rule & number>
295+
: kind extends "exactLength" ? ExactlyLength<rule & number>
285296
: kind extends "after" ?
286297
schema extends { exclusive: true } ?
287298
After<normalizeLimit<rule>>

ark/schema/constraint.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
throwInternalError,
77
throwParseError,
88
type array,
9-
type describeExpression,
9+
type describe,
1010
type listable,
1111
type satisfy
1212
} from "@arktype/util"
@@ -247,9 +247,9 @@ export const writeInvalidOperandMessage = <
247247
export type writeInvalidOperandMessage<
248248
kind extends ConstraintKind,
249249
actual extends Root
250-
> = `${Capitalize<kind>} operand must be ${describeExpression<
250+
> = `${Capitalize<kind>} operand must be ${describe<
251251
Prerequisite<kind>
252-
>} (was ${describeExpression<Exclude<actual["infer"], Prerequisite<kind>>>})`
252+
>} (was ${describe<Exclude<actual["infer"], Prerequisite<kind>>>})`
253253

254254
export interface ConstraintAttachments {
255255
impliedBasis: UnknownRoot | null

ark/schema/keywords/internal.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { arrayIndexMatcher } from "../structure/shared.js"
66
import "./tsKeywords.js"
77

88
export interface internalKeywordExports {
9-
lengthBoundable: string | unknown[]
9+
lengthBoundable: string | readonly unknown[]
1010
propertyKey: Key
1111
nonNegativeIntegerString: string
1212
}

ark/schema/predicate.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@ import type {
1111
TraverseApply
1212
} from "./shared/traversal.js"
1313

14-
export interface PredicateInner<rule extends Predicate<any> = Predicate<any>>
14+
export interface PredicateInner<predicate extends Predicate = Predicate>
1515
extends BaseMeta {
16-
readonly predicate: rule
16+
readonly predicate: predicate
1717
}
1818

1919
export type PredicateErrorContext = Partial<PredicateInner>
2020

21-
export type PredicateSchema = PredicateInner | Predicate<any>
21+
export type PredicateSchema<predicate extends Predicate = Predicate> =
22+
| PredicateInner<predicate>
23+
| predicate
2224

2325
export interface PredicateDeclaration
2426
extends declareNode<{
@@ -87,7 +89,7 @@ export class PredicateNode extends BaseConstraint<PredicateDeclaration> {
8789
}
8890
}
8991

90-
export type Predicate<data = unknown> = (
92+
export type Predicate<data = any> = (
9193
data: data,
9294
ctx: TraversalContext
9395
) => boolean
@@ -97,7 +99,7 @@ export type PredicateCast<input = never, narrowed extends input = input> = (
9799
ctx: TraversalContext
98100
) => input is narrowed
99101

100-
export type inferNarrow<t, predicate> =
102+
export type inferPredicate<t, predicate> =
101103
predicate extends (data: any, ...args: any[]) => data is infer narrowed ?
102104
t extends of<unknown, infer constraints> ?
103105
constrain<of<narrowed, constraints>, "predicate", any>

ark/schema/refinements/after.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,20 @@ import {
99
BaseRange,
1010
parseDateLimit,
1111
parseExclusiveKey,
12-
type BaseNormalizedRangeRoot,
1312
type BaseRangeInner,
14-
type LimitRootValue
13+
type LimitSchemaValue,
14+
type UnknownNormalizedRangeSchema
1515
} from "./range.js"
1616

1717
export interface AfterInner extends BaseRangeInner {
1818
rule: Date
1919
}
2020

21-
export interface NormalizedAfterSchema extends BaseNormalizedRangeRoot {
22-
rule: LimitRootValue
21+
export interface NormalizedAfterSchema extends UnknownNormalizedRangeSchema {
22+
rule: LimitSchemaValue
2323
}
2424

25-
export type AfterSchema = NormalizedAfterSchema | LimitRootValue
25+
export type AfterSchema = NormalizedAfterSchema | LimitSchemaValue
2626

2727
export interface AfterDeclaration
2828
extends declareNode<{

ark/schema/refinements/before.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,20 @@ import {
1010
BaseRange,
1111
parseDateLimit,
1212
parseExclusiveKey,
13-
type BaseNormalizedRangeRoot,
1413
type BaseRangeInner,
15-
type LimitRootValue
14+
type LimitSchemaValue,
15+
type UnknownNormalizedRangeSchema
1616
} from "./range.js"
1717

1818
export interface BeforeInner extends BaseRangeInner {
1919
rule: Date
2020
}
2121

22-
export interface NormalizedBeforeSchema extends BaseNormalizedRangeRoot {
23-
rule: LimitRootValue
22+
export interface NormalizedBeforeSchema extends UnknownNormalizedRangeSchema {
23+
rule: LimitSchemaValue
2424
}
2525

26-
export type BeforeSchema = NormalizedBeforeSchema | LimitRootValue
26+
export type BeforeSchema = NormalizedBeforeSchema | LimitSchemaValue
2727

2828
export interface BeforeDeclaration
2929
extends declareNode<{

0 commit comments

Comments
 (0)