Skip to content

Commit

Permalink
feat: generic hkts, partial, required, onDeepUndeclaredKey (#1068)
Browse files Browse the repository at this point in the history
  • Loading branch information
ssalbdivad authored Jul 26, 2024
1 parent 736feb4 commit a8e2a74
Show file tree
Hide file tree
Showing 37 changed files with 725 additions and 276 deletions.
2 changes: 1 addition & 1 deletion ark/attest/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ark/attest",
"version": "0.10.0",
"version": "0.10.1",
"author": {
"name": "David Blass",
"email": "david@arktype.io",
Expand Down
4 changes: 2 additions & 2 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": "Syntax highlighting, inline errors and theme for ArkType⛵",
"version": "5.4.2",
"version": "5.5.0",
"publisher": "arktypeio",
"type": "module",
"scripts": {
Expand Down Expand Up @@ -67,7 +67,7 @@
},
"errorLens.replace": [
{
"matcher": ".*'keyError<\"(.*)\">'\\.$",
"matcher": ".*'ErrorType<(.*)>'\\.$",
"message": "$1"
},
{
Expand Down
2 changes: 1 addition & 1 deletion ark/fs/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ark/fs",
"version": "0.1.1",
"version": "0.1.2",
"author": {
"name": "David Blass",
"email": "david@arktype.io",
Expand Down
4 changes: 0 additions & 4 deletions ark/repo/scratch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,3 @@ const t = type({
const o = type("string")

ark.Record

// ("string", {
// bar: "number | string"
// })
48 changes: 28 additions & 20 deletions ark/schema/generic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@ import {
snapshot,
throwParseError,
type array,
type Hkt,
type Json
} from "@ark/util"
import type { inferRoot } from "./inference.js"
import type { RootSchema } from "./kinds.js"
import type { SchemaRoot, UnknownRoot } from "./roots/root.js"
import type { BaseScope, InternalBaseScope } from "./scope.js"
import { arkKind } from "./shared/utils.js"
Expand Down Expand Up @@ -118,9 +116,9 @@ export type GenericArgResolutions<
}

export class LazyGenericBody<
params extends array<GenericParamAst> = array<GenericParamAst>,
argResolutions = {},
returns = unknown
> extends Callable<(args: GenericArgResolutions<params>) => returns> {}
> extends Callable<(args: argResolutions) => returns> {}

export class GenericRoot<
params extends array<GenericParamAst> = array<GenericParamAst>,
Expand Down Expand Up @@ -210,7 +208,7 @@ export class GenericRoot<

@cached
get baseInstantiation(): SchemaRoot {
return this(...(this.constraints as never))
return this(...(this.constraints as never)) as never
}

validateBaseInstantiation(): this {
Expand All @@ -231,24 +229,34 @@ export type GenericHktSchemaParser<$ = {}> = <
const paramsDef extends array<GenericParamDef>
>(
...params: paramsDef
) => (
instantiateDef: LazyGenericBody<
genericParamSchemasToAst<paramsDef, $>,
RootSchema
>
) => GenericHktRootSubclass<genericParamSchemasToAst<paramsDef, $>, $>
) => <
hkt extends abstract new () => GenericHkt,
params extends Array<GenericParamAst> = genericParamSchemasToAst<paramsDef, $>
>(
instantiateDef: LazyGenericBody<GenericArgResolutions<params>>,
hkt: hkt
) => GenericRoot<params, InstanceType<hkt>, $, $>

export type GenericHktRootSubclass<
params extends array<GenericParamAst>,
$
> = abstract new () => GenericHktRoot<params, $, $>
export abstract class GenericHkt<
hkt extends (args: any) => unknown = (args: any) => unknown
> {
declare readonly args: array
abstract readonly hkt: hkt
}

// convenient for AST display without including default params
interface Hkt extends Hkt.Kind {}
export namespace GenericHkt {
export type instantiate<
hkt extends GenericHkt,
args extends Parameters<hkt["hkt"]>[0]
> = ReturnType<
(hkt & {
readonly args: args
})["hkt"]
>

export interface GenericHktRoot<params extends array<GenericParamAst>, $, args$>
extends GenericRoot<params, Hkt, $, args$>,
Hkt.Kind {}
export type conform<thisArgs, parameters extends array> =
thisArgs extends parameters ? thisArgs : parameters
}

export const writeUnsatisfiedParameterConstraintMessage = <
name extends string,
Expand Down
28 changes: 28 additions & 0 deletions ark/schema/keywords/arkGenerics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { liftArray, type conform } from "@ark/util"
import { GenericHkt } from "../generic.js"
import type { SchemaModule } from "../module.js"
import type { Out } from "../roots/morph.js"
import { generic, schemaScope } from "../scope.js"

const ArkLiftArray = generic("T")(
args => args.T.or(args.T.array()).pipe(liftArray),
class liftArrayHkt extends GenericHkt {
declare hkt: (
args: conform<this["args"], [unknown]>
) => liftArray<(typeof args)[0]> extends infer lifted ?
(In: (typeof args)[0] | lifted) => Out<lifted>
: never
}
)

const arkGenericsExports = {
liftArray: ArkLiftArray
}

export type arkGenericsExports = typeof arkGenericsExports

export type arkGenerics = SchemaModule<arkGenericsExports>

const $ = schemaScope(arkGenericsExports)

export const arkGenerics: arkGenerics = $.export()
3 changes: 3 additions & 0 deletions ark/schema/keywords/keywords.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { schemaScope, type BaseScope } from "../scope.js"
import { tsKeywords, type tsKeywordExports } from "./tsKeywords.js"

import { $ark } from "@ark/util"
import { arkGenerics, type arkGenericsExports } from "./arkGenerics.js"
import { formatting, type formattingExports } from "./format.js"
import { internal, type internalExports } from "./internal.js"
import { jsObjects, type jsObjectExports } from "./jsObjects.js"
Expand All @@ -24,6 +25,7 @@ export const ambientRootScope: BaseScope<Ark> = schemaScope({
...validation,
...internal,
...tsGenerics,
...arkGenerics,
TypedArray: typedArray,
parse: parsing,
format: formatting
Expand All @@ -41,6 +43,7 @@ export interface Ark
platformObjectExports,
validationExports,
tsGenericsExports,
arkGenericsExports,
internalExports {
TypedArray: RootModule<typedArrayExports>
parse: RootModule<parsingExports>
Expand Down
132 changes: 73 additions & 59 deletions ark/schema/keywords/tsGenerics.ts
Original file line number Diff line number Diff line change
@@ -1,77 +1,91 @@
import {
$ark,
liftArray,
type conform,
type Hkt,
type Key,
type show
} from "@ark/util"
import { $ark, type conform, type Key, type show } from "@ark/util"
import { GenericHkt } from "../generic.js"
import type { SchemaModule } from "../module.js"
import type { Out } from "../roots/morph.js"
import { generic, schemaScope } from "../scope.js"

class ArkRecord extends generic(
["K", $ark.intrinsic.propertyKey],
"V"
)(args => ({
domain: "object",
index: {
signature: args.K,
value: args.V
const Record = generic(["K", $ark.intrinsic.propertyKey], "V")(
args => ({
domain: "object",
index: {
signature: args.K,
value: args.V
}
}),
class RecordHkt extends GenericHkt {
declare hkt: (
args: conform<this["args"], [PropertyKey, unknown]>
) => Record<(typeof args)[0], (typeof args)[1]>
}
})) {
declare hkt: (
args: conform<this[Hkt.args], [PropertyKey, unknown]>
) => Record<(typeof args)[0], (typeof args)[1]>
}
)

class ArkPick extends generic(
const Pick = generic(
["T", $ark.intrinsic.object],
["K", $ark.intrinsic.propertyKey]
)(args => args.T.pick(args.K as never)) {
declare hkt: (
args: conform<this[Hkt.args], [object, Key]>
) => show<Pick<(typeof args)[0], (typeof args)[1] & keyof (typeof args)[0]>>
}
)(
args => args.T.pick(args.K as never),
class PickHkt extends GenericHkt {
declare hkt: (
args: conform<this["args"], [object, Key]>
) => show<Pick<(typeof args)[0], (typeof args)[1] & keyof (typeof args)[0]>>
}
)

class ArkOmit extends generic(
const Omit = generic(
["T", $ark.intrinsic.object],
["K", $ark.intrinsic.propertyKey]
)(args => args.T.omit(args.K as never)) {
declare hkt: (
args: conform<this[Hkt.args], [object, Key]>
) => show<Omit<(typeof args)[0], (typeof args)[1] & keyof (typeof args)[0]>>
}
)(
args => args.T.omit(args.K as never),
class OmitHkt extends GenericHkt {
declare hkt: (
args: conform<this["args"], [object, Key]>
) => show<Omit<(typeof args)[0], (typeof args)[1] & keyof (typeof args)[0]>>
}
)

class ArkExclude extends generic("T", "U")(args => args.T.exclude(args.U)) {
declare hkt: (
args: conform<this[Hkt.args], [unknown, unknown]>
) => Exclude<(typeof args)[0], (typeof args)[1]>
}
const Partial = generic(["T", $ark.intrinsic.object])(
args => args.T.partial(),
class PartialHkt extends GenericHkt {
declare hkt: (
args: conform<this["args"], [object]>
) => show<Partial<(typeof args)[0]>>
}
)

class ArkExtract extends generic("T", "U")(args => args.T.extract(args.U)) {
declare hkt: (
args: conform<this[Hkt.args], [unknown, unknown]>
) => Extract<(typeof args)[0], (typeof args)[1]>
}
const Required = generic(["T", $ark.intrinsic.object])(
args => args.T.required(),
class RequiredHkt extends GenericHkt {
declare hkt: (
args: conform<this["args"], [object]>
) => show<Required<(typeof args)[0]>>
}
)

class ArkLiftArray extends generic("T")(args =>
args.T.or(args.T.array()).pipe(liftArray)
) {
declare hkt: (
args: conform<this[Hkt.args], [unknown]>
) => liftArray<(typeof args)[0]> extends infer lifted ?
(In: (typeof args)[0] | lifted) => Out<lifted>
: never
}
const Exclude = generic("T", "U")(
args => args.T.exclude(args.U),
class ExcludeHkt extends GenericHkt {
declare hkt: (
args: conform<this["args"], [unknown, unknown]>
) => Exclude<(typeof args)[0], (typeof args)[1]>
}
)

const Extract = generic("T", "U")(
args => args.T.extract(args.U),
class ExtractHkt extends GenericHkt {
declare hkt: (
args: conform<this["args"], [unknown, unknown]>
) => Extract<(typeof args)[0], (typeof args)[1]>
}
)

const tsGenericsExports = {
Record: new ArkRecord(),
Pick: new ArkPick(),
Omit: new ArkOmit(),
Exclude: new ArkExclude(),
Extract: new ArkExtract(),
liftArray: new ArkLiftArray()
Record,
Pick,
Omit,
Exclude,
Extract,
Partial,
Required
}

export type tsGenericsExports = typeof tsGenericsExports
Expand Down
2 changes: 1 addition & 1 deletion ark/schema/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ark/schema",
"version": "0.2.1",
"version": "0.2.2",
"license": "MIT",
"author": {
"name": "David Blass",
Expand Down
14 changes: 8 additions & 6 deletions ark/schema/roots/intersection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,14 @@ export type NormalizedIntersectionSchema = Omit<
StructuralKind | "undeclared"
>

export type IntersectionSchema<inferredBasis = any> = show<
BaseMeta & {
domain?: DomainSchema
proto?: ProtoSchema
} & conditionalRootOf<inferredBasis>
>
export type IntersectionSchema<inferredBasis = any> =
| show<
BaseMeta & {
domain?: DomainSchema
proto?: ProtoSchema
} & conditionalRootOf<inferredBasis>
>
| IntersectionNode

export type IntersectionDeclaration = declareNode<{
kind: "intersection"
Expand Down
3 changes: 2 additions & 1 deletion ark/schema/roots/morph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ export const morphImplementation: nodeImplementationOf<MorphDeclaration> =
)
: ctx.$.node("morph", {
...l.inner,
in: inTersection
// TODO: https://github.com/arktypeio/arktype/issues/1067
in: inTersection as MorphChildNode
})
)
})
Expand Down
Loading

0 comments on commit a8e2a74

Please sign in to comment.