Skip to content

Commit

Permalink
feat: chainable constraints (#995)
Browse files Browse the repository at this point in the history
  • Loading branch information
ssalbdivad authored Jun 1, 2024
1 parent de3867d commit 317f012
Show file tree
Hide file tree
Showing 29 changed files with 685 additions and 113 deletions.
5 changes: 5 additions & 0 deletions .changeset/cuddly-dingos-approve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@arktype/util": patch
---

Add `leftIfEqual` type utility
5 changes: 5 additions & 0 deletions .changeset/tender-wombats-march.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@arktype/schema": patch
---

Fixed a cyclic traversal case (see [arktype CHANGELOG](../type/CHANGELOG.md))
6 changes: 3 additions & 3 deletions ark/dark/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "arkdark",
"displayName": "ArkDark",
"description": "ArkType syntax highlighting and theme⛵",
"version": "5.2.2",
"version": "5.2.3",
"publisher": "arktypeio",
"type": "module",
"scripts": {
Expand Down Expand Up @@ -59,11 +59,11 @@
},
"errorLens.replace": [
{
"matcher": "^(?:Type|Argument of type) '.*' is not assignable to (?:parameter of )?type 'keyError<\"(.*)\">'\\.$",
"matcher": "'keyError<\"(.*)\">'\\.$",
"message": "$1"
},
{
"matcher": "^(?:Type|Argument of type) '.*' is not assignable to (?:parameter of )?type '\"(.*\\u200A)\"'\\.$",
"matcher": "'\"(.*\\u200A)\"'\\.$",
"message": "$1"
},
{
Expand Down
1 change: 1 addition & 0 deletions ark/schema/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export * from "./predicate.js"
export * from "./refinements/after.js"
export * from "./refinements/before.js"
export * from "./refinements/divisor.js"
export * from "./refinements/exactLength.js"
export * from "./refinements/max.js"
export * from "./refinements/maxLength.js"
export * from "./refinements/min.js"
Expand Down
23 changes: 17 additions & 6 deletions ark/schema/ast.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { conform } from "@arktype/util"
import type { conform, leftIfEqual } from "@arktype/util"
import type { PrimitiveConstraintKind } from "./constraint.js"
import type { NodeSchema } from "./kinds.js"
import type { constraintKindOf } from "./roots/intersection.js"
import type { MorphAst, Out } from "./roots/morph.js"

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

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

export type ExactlyLength<rule extends number> = {
min: { [k in rule]: 1 }
max: { [k in rule]: 1 }
}

export namespace string {
export type atLeastLength<rule extends number> = of<
string,
Expand All @@ -144,6 +150,11 @@ export namespace string {
LessThanLength<rule>
>

export type exactlyLength<rule extends number> = of<
string,
ExactlyLength<rule>
>

export type matching<rule extends string> = of<string, Matching<rule>>

export type narrowed = of<string, Narrowed>
Expand All @@ -164,6 +175,7 @@ export namespace string {
lessThanLength<rule & number>
: atMostLength<rule & number>
: kind extends "regex" ? matching<rule & string>
: kind extends "exactLength" ? exactlyLength<rule & number>
: narrowed
: never
}
Expand Down Expand Up @@ -227,11 +239,9 @@ export type constrain<
kind extends PrimitiveConstraintKind,
schema extends NodeSchema<kind>
> =
_constrain<t, kind, schema> extends infer constrained ?
[t, constrained] extends [constrained, t] ?
t
: constrained
: never
t extends MorphAst<infer i, infer o> ?
(In: leftIfEqual<i, _constrain<i, kind, schema>>) => Out<o>
: leftIfEqual<t, _constrain<t, kind, schema>>

type _constrain<
t,
Expand Down Expand Up @@ -282,6 +292,7 @@ export type schemaToConstraint<
schema extends { exclusive: true } ?
LessThanLength<rule & number>
: AtMostLength<rule & number>
: kind extends "exactLength" ? ExactlyLength<rule & number>
: kind extends "after" ?
schema extends { exclusive: true } ?
After<normalizeLimit<rule>>
Expand Down
6 changes: 3 additions & 3 deletions ark/schema/constraint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
throwInternalError,
throwParseError,
type array,
type describeExpression,
type describe,
type listable,
type satisfy
} from "@arktype/util"
Expand Down Expand Up @@ -247,9 +247,9 @@ export const writeInvalidOperandMessage = <
export type writeInvalidOperandMessage<
kind extends ConstraintKind,
actual extends Root
> = `${Capitalize<kind>} operand must be ${describeExpression<
> = `${Capitalize<kind>} operand must be ${describe<
Prerequisite<kind>
>} (was ${describeExpression<Exclude<actual["infer"], Prerequisite<kind>>>})`
>} (was ${describe<Exclude<actual["infer"], Prerequisite<kind>>>})`

export interface ConstraintAttachments {
impliedBasis: UnknownRoot | null
Expand Down
2 changes: 1 addition & 1 deletion ark/schema/keywords/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { arrayIndexMatcher } from "../structure/shared.js"
import "./tsKeywords.js"

export interface internalKeywordExports {
lengthBoundable: string | unknown[]
lengthBoundable: string | readonly unknown[]
propertyKey: Key
nonNegativeIntegerString: string
}
Expand Down
12 changes: 7 additions & 5 deletions ark/schema/predicate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ import type {
TraverseApply
} from "./shared/traversal.js"

export interface PredicateInner<rule extends Predicate<any> = Predicate<any>>
export interface PredicateInner<predicate extends Predicate = Predicate>
extends BaseMeta {
readonly predicate: rule
readonly predicate: predicate
}

export type PredicateErrorContext = Partial<PredicateInner>

export type PredicateSchema = PredicateInner | Predicate<any>
export type PredicateSchema<predicate extends Predicate = Predicate> =
| PredicateInner<predicate>
| predicate

export interface PredicateDeclaration
extends declareNode<{
Expand Down Expand Up @@ -87,7 +89,7 @@ export class PredicateNode extends BaseConstraint<PredicateDeclaration> {
}
}

export type Predicate<data = unknown> = (
export type Predicate<data = any> = (
data: data,
ctx: TraversalContext
) => boolean
Expand All @@ -97,7 +99,7 @@ export type PredicateCast<input = never, narrowed extends input = input> = (
ctx: TraversalContext
) => input is narrowed

export type inferNarrow<t, predicate> =
export type inferPredicate<t, predicate> =
predicate extends (data: any, ...args: any[]) => data is infer narrowed ?
t extends of<unknown, infer constraints> ?
constrain<of<narrowed, constraints>, "predicate", any>
Expand Down
10 changes: 5 additions & 5 deletions ark/schema/refinements/after.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@ import {
BaseRange,
parseDateLimit,
parseExclusiveKey,
type BaseNormalizedRangeRoot,
type BaseRangeInner,
type LimitRootValue
type LimitSchemaValue,
type UnknownNormalizedRangeSchema
} from "./range.js"

export interface AfterInner extends BaseRangeInner {
rule: Date
}

export interface NormalizedAfterSchema extends BaseNormalizedRangeRoot {
rule: LimitRootValue
export interface NormalizedAfterSchema extends UnknownNormalizedRangeSchema {
rule: LimitSchemaValue
}

export type AfterSchema = NormalizedAfterSchema | LimitRootValue
export type AfterSchema = NormalizedAfterSchema | LimitSchemaValue

export interface AfterDeclaration
extends declareNode<{
Expand Down
10 changes: 5 additions & 5 deletions ark/schema/refinements/before.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@ import {
BaseRange,
parseDateLimit,
parseExclusiveKey,
type BaseNormalizedRangeRoot,
type BaseRangeInner,
type LimitRootValue
type LimitSchemaValue,
type UnknownNormalizedRangeSchema
} from "./range.js"

export interface BeforeInner extends BaseRangeInner {
rule: Date
}

export interface NormalizedBeforeSchema extends BaseNormalizedRangeRoot {
rule: LimitRootValue
export interface NormalizedBeforeSchema extends UnknownNormalizedRangeSchema {
rule: LimitSchemaValue
}

export type BeforeSchema = NormalizedBeforeSchema | LimitRootValue
export type BeforeSchema = NormalizedBeforeSchema | LimitSchemaValue

export interface BeforeDeclaration
extends declareNode<{
Expand Down
6 changes: 3 additions & 3 deletions ark/schema/refinements/max.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ import {
} from "../shared/implement.js"
import type { TraverseAllows } from "../shared/traversal.js"
import {
type BaseNormalizedRangeRoot,
BaseRange,
type BaseRangeInner,
parseExclusiveKey
parseExclusiveKey,
type UnknownNormalizedRangeSchema
} from "./range.js"

export interface MaxInner extends BaseRangeInner {
rule: number
}

export interface NormalizedMaxSchema extends BaseNormalizedRangeRoot {
export interface NormalizedMaxSchema extends UnknownNormalizedRangeSchema {
rule: number
}

Expand Down
7 changes: 4 additions & 3 deletions ark/schema/refinements/maxLength.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,19 @@ import {
} from "../shared/implement.js"
import type { TraverseAllows } from "../shared/traversal.js"
import {
type BaseNormalizedRangeRoot,
BaseRange,
type BaseRangeInner,
type LengthBoundableData,
parseExclusiveKey
parseExclusiveKey,
type UnknownNormalizedRangeSchema
} from "./range.js"

export interface MaxLengthInner extends BaseRangeInner {
rule: number
}

export interface NormalizedMaxLengthSchema extends BaseNormalizedRangeRoot {
export interface NormalizedMaxLengthSchema
extends UnknownNormalizedRangeSchema {
rule: number
}

Expand Down
10 changes: 5 additions & 5 deletions ark/schema/refinements/min.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,26 @@ import {
} from "../shared/implement.js"
import type { TraverseAllows } from "../shared/traversal.js"
import {
type BaseNormalizedRangeRoot,
BaseRange,
type BaseRangeInner,
parseExclusiveKey
parseExclusiveKey,
type UnknownNormalizedRangeSchema
} from "./range.js"

export interface MinInner extends BaseRangeInner {
rule: number
}

export interface NormalizedMinRoot extends BaseNormalizedRangeRoot {
export interface NormalizedMinRoot extends UnknownNormalizedRangeSchema {
rule: number
}

export type MinRoot = NormalizedMinRoot | number
export type MinSchema = NormalizedMinRoot | number

export interface MinDeclaration
extends declareNode<{
kind: "min"
schema: MinRoot
schema: MinSchema
normalizedSchema: NormalizedMinRoot
inner: MinInner
prerequisite: number
Expand Down
7 changes: 4 additions & 3 deletions ark/schema/refinements/minLength.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,19 @@ import {
} from "../shared/implement.js"
import type { TraverseAllows } from "../shared/traversal.js"
import {
type BaseNormalizedRangeRoot,
BaseRange,
type BaseRangeInner,
type LengthBoundableData,
parseExclusiveKey
parseExclusiveKey,
type UnknownNormalizedRangeSchema
} from "./range.js"

export interface MinLengthInner extends BaseRangeInner {
rule: number
}

export interface NormalizedMinLengthSchema extends BaseNormalizedRangeRoot {
export interface NormalizedMinLengthSchema
extends UnknownNormalizedRangeSchema {
rule: number
}

Expand Down
Loading

0 comments on commit 317f012

Please sign in to comment.