Socket
Socket
Sign inDemoInstall

arktype

Package Overview
Dependencies
Maintainers
1
Versions
100
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

arktype - npm Package Compare versions

Comparing version 2.0.0-dev.2 to 2.0.0-dev.3

307

__tests__/array.test.ts

@@ -7,3 +7,3 @@ import { attest } from "@arktype/attest"

multipleVariadicMesage,
writeNonArrayRestMessage
writeNonArraySpreadMessage
} from "../parser/tuple.js"

@@ -113,13 +113,19 @@

attest(t.allows(["", 0])).equals(true)
attest(t(["", 0]).out).snap()
attest(t(["", 0]).out).snap(["", 0])
attest(t.allows([true, 0])).equals(false)
attest(t([true, 0]).errors?.summary).snap()
attest(t([true, 0]).errors?.summary).snap(
"Value at [0] must be a string (was boolean)"
)
attest(t.allows([0, false])).equals(false)
attest(t([0, false]).errors?.summary).snap()
attest(t([0, false]).errors?.summary)
.snap(`Value at [0] must be a string (was number)
Value at [1] must be a number (was boolean)`)
// too short
attest(t.allows([""])).equals(false)
attest(t([""]).errors?.summary).snap()
attest(t([""]).errors?.summary).snap("Must be at least length 2 (was 1)")
// too long
attest(t.allows(["", 0, 1])).equals(false)
attest(t(["", 0, 1]).errors?.summary).snap()
attest(t(["", 0, 1]).errors?.summary).snap(
"Must be at most length 2 (was 3)"
)
// non-array

@@ -139,7 +145,7 @@ attest(

}).errors?.summary
).snap()
).snap("Must be an array (was object)")
})
it("nested", () => {
const t = type([["string", "number"], [{ a: "boolean", b: ["null"] }]])
const t = type([["string", "number"], [{ a: "bigint", b: ["null"] }]])
attest<

@@ -150,3 +156,3 @@ [

{
a: boolean
a: bigint
b: [null]

@@ -157,67 +163,62 @@ }

>(t.infer)
attest(t.allows([["", 0], [{ a: true, b: [null] }]])).equals(true)
attest(t([["foo", 1], [{ a: true, b: [null] }]]).out).snap([
["foo", 1],
[{ a: true, b: [null] }]
])
attest(t.allows([["", 0], [{ a: true, b: [undefined] }]])).equals(false)
attest(
t([["foo", 1], [{ a: true, b: [undefined] }]]).errors?.summary
).snap("[1][0].b[0] must be null (was undefined)")
const valid: typeof t.infer = [["", 0], [{ a: 0n, b: [null] }]]
attest(t.allows(valid)).equals(true)
attest(t(valid).out).equals(valid)
const invalid = [["", 0], [{ a: 0n, b: [undefined] }]]
attest(t.allows(invalid)).equals(false)
attest(t(invalid).errors?.summary).snap(
"Value at [1][0].b[0] must be null (was undefined)"
)
})
})
describe("variadic tuple", () => {
describe("variadic", () => {
it("spreads simple arrays", () => {
const wellRested = type(["string", "...number[]"])
attest<[string, ...number[]]>(wellRested.infer)
attest(wellRested(["foo"]).out).equals(["foo"])
attest(wellRested(["foo", 1, 2]).out).equals(["foo", 1, 2])
})
it("tuple expression", () => {
const wellRestedTuple = type([
"number",
["...", [{ a: "string" }, "[]"]]
it("spreads simple arrays", () => {
const wellRested = type(["string", "...number[]"])
attest<[string, ...number[]]>(wellRested.infer)
attest(wellRested(["foo"]).out).equals(["foo"])
attest(wellRested(["foo", 1, 2]).out).equals(["foo", 1, 2])
})
it("tuple expression", () => {
const wellRestedTuple = type(["number", ["...", [{ a: "string" }, "[]"]]])
attest<[number, ...{ a: string }[]]>(wellRestedTuple.infer)
})
it("spreads array expressions", () => {
const greatSpread = type(["0", "...(Date|RegExp)[]"])
attest<[0, ...(RegExp | Date)[]]>(greatSpread.infer)
})
it("distributes spread unions", () => {
const t = type(["1", "...(Date[] | RegExp[])"])
attest<[1, ...(Date[] | RegExp[])]>(t.infer)
const expected = type(["1", "...Date[]"]).or(["1", "...RegExp[]"])
attest(t.json).equals(expected.json)
})
it("allows array keyword", () => {
const types = scope({
myArrayKeyword: "boolean[]",
myVariadicKeyword: ["string", "...myArrayKeyword"]
}).export()
attest<[string, ...boolean[]]>(types.myVariadicKeyword.infer)
})
it("errors on non-array", () => {
attest(() =>
// @ts-expect-error
type(["email", "...symbol"])
).throwsAndHasTypeError(writeNonArraySpreadMessage("a symbol"))
attest(() =>
// @ts-expect-error
type(["number", ["...", "string"]])
).throwsAndHasTypeError(writeNonArraySpreadMessage("a string"))
})
it("errors on multiple variadic", () => {
attest(() =>
// @ts-expect-error
type(["...number[]", "...string[]"])
).throwsAndHasTypeError(multipleVariadicMesage)
attest(() =>
type([
["...", "string[]"],
// @ts-expect-error
["...", "number[]"]
])
attest<[number, ...{ a: string }[]]>(wellRestedTuple.infer)
})
it("spreads array expressions", () => {
const greatSpread = type([{ a: "boolean" }, "...(Date|RegExp)[]"])
attest<
[
{
a: boolean
},
...(RegExp | Date)[]
]
>(greatSpread.infer)
})
it("allows array keyword", () => {
const types = scope({
myArrayKeyword: "boolean[]",
myVariadicKeyword: ["string", "...myArrayKeyword"]
}).export()
attest<[string, ...boolean[]]>(types.myVariadicKeyword.infer)
})
it("errors on non-array", () => {
attest(() =>
// @ts-expect-error
type(["email", "...symbol"])
).throwsAndHasTypeError(writeNonArrayRestMessage("symbol"))
attest(() =>
// @ts-expect-error
type(["number", ["...", "string"]])
).throwsAndHasTypeError(writeNonArrayRestMessage("string"))
})
it("errors on non-last element", () => {
attest(() =>
// @ts-expect-error
type(["...number[]", "string"])
).throwsAndHasTypeError(multipleVariadicMesage)
attest(() =>
// @ts-expect-error
type([["...", "string[]"], "number"])
).throwsAndHasTypeError(multipleVariadicMesage)
})
).throwsAndHasTypeError(multipleVariadicMesage)
})

@@ -227,13 +228,10 @@ })

it("shallow array intersection", () => {
const actual = type("string[]&'foo'[]").json
const expected = type("'foo'[]").json
attest(actual).is(expected)
const t = type("string[]&'foo'[]")
const expected = type("'foo'[]")
attest(t.json).equals(expected.json)
})
it("deep array intersection", () => {
const actual = type([{ a: "string" }, "[]"]).and([
{ b: "number" },
"[]"
]).json
const expected = type([{ a: "string", b: "number" }, "[]"]).json
attest(actual).is(expected)
const t = type([{ a: "string" }, "[]"]).and([{ b: "number" }, "[]"])
const expected = type([{ a: "string", b: "number" }, "[]"])
attest(t.json).equals(expected.json)
})

@@ -250,2 +248,3 @@ it("tuple intersection", () => {

>(t.infer)
const expected = type([{ a: "string", b: "boolean" }])
})

@@ -281,5 +280,5 @@ it("tuple and array", () => {

const expected = type([{ a: "string", b: "boolean" }]).json
attest(tupleAndArray.json).is(expected)
attest(arrayAndTuple.json).is(expected)
const expected = type([{ a: "string", b: "boolean" }])
attest(tupleAndArray.json).equals(expected.json)
attest(arrayAndTuple.json).equals(expected.json)
})

@@ -308,2 +307,146 @@ it("variadic and tuple", () => {

})
// based on the equivalent type-level test from @arktype/util
it("kitchen sink", () => {
const l = type([
{ a: "0" },
[{ b: "1" }, "?"],
[{ c: "2" }, "?"],
["...", [{ d: "3" }, "[]"]]
])
const r = type([
[{ e: "4" }, "?"],
[{ f: "5" }, "?"],
["...", [{ g: "6" }, "[]"]]
])
const result = l.and(r)
const expected = type([
{ a: "0", e: "4" },
[{ b: "1", f: "5" }, "?"],
[{ c: "2", g: "6" }, "?"],
["...", [{ d: "3", g: "6" }, "[]"]]
])
attest<typeof expected>(result)
attest(result.json).equals(expected.json)
})
it("prefix and postfix", () => {
const l = type([["...", [{ a: "0" }, "[]"]], { b: "0" }, { c: "0" }])
const r = type([{ x: "0" }, { y: "0" }, ["...", [{ z: "0" }, "[]"]]])
// currently getting this "Expected" result at a type-level incurs
// too high a performance cost for such a niche intersection.
type Expected =
| [
{ a: 0; x: 0 },
{ a: 0; y: 0 },
...{ a: 0; z: 0 }[],
{ b: 0; z: 0 },
{ c: 0; z: 0 }
]
| [{ a: 0; x: 0 }, { b: 0; y: 0 }, { c: 0; z: 0 }]
| [{ b: 0; x: 0 }, { c: 0; y: 0 }]
const result = l.and(r)
attest(result.json).snap([
{
basis: "Array",
maxLength: 2,
minLength: 2,
sequence: {
prefix: [
{
basis: "object",
required: [
{ key: "b", value: { unit: 0 } },
{ key: "x", value: { unit: 0 } }
]
},
{
basis: "object",
required: [
{ key: "c", value: { unit: 0 } },
{ key: "y", value: { unit: 0 } }
]
}
]
}
},
{
basis: "Array",
maxLength: 3,
minLength: 3,
sequence: {
prefix: [
{
basis: "object",
required: [
{ key: "a", value: { unit: 0 } },
{ key: "x", value: { unit: 0 } }
]
},
{
basis: "object",
required: [
{ key: "b", value: { unit: 0 } },
{ key: "y", value: { unit: 0 } }
]
},
{
basis: "object",
required: [
{ key: "c", value: { unit: 0 } },
{ key: "z", value: { unit: 0 } }
]
}
]
}
},
{
basis: "Array",
minLength: 4,
sequence: {
postfix: [
{
basis: "object",
required: [
{ key: "b", value: { unit: 0 } },
{ key: "z", value: { unit: 0 } }
]
},
{
basis: "object",
required: [
{ key: "c", value: { unit: 0 } },
{ key: "z", value: { unit: 0 } }
]
}
],
prefix: [
{
basis: "object",
required: [
{ key: "a", value: { unit: 0 } },
{ key: "x", value: { unit: 0 } }
]
},
{
basis: "object",
required: [
{ key: "a", value: { unit: 0 } },
{ key: "y", value: { unit: 0 } }
]
}
],
variadic: {
basis: "object",
required: [
{ key: "a", value: { unit: 0 } },
{ key: "z", value: { unit: 0 } }
]
}
}
}
])
})
})

@@ -310,0 +453,0 @@ // TODO: reenable

@@ -12,2 +12,6 @@ import { attest } from "@arktype/attest"

attest(divisibleByTwo.json).snap({ basis: "number", divisor: 2 })
attest(divisibleByTwo(4).out).snap(4)
attest(divisibleByTwo(5).errors?.summary).snap(
"Must be a multiple of 2 (was 5)"
)
})

@@ -86,4 +90,4 @@ it("whitespace after %", () => {

it("invalid literal", () => {
attest(() => type("number%3&8")).throws(
"Intersection of (a multiple of 3) and 8 results in an unsatisfiable type"
attest(() => type("number%3&8")).throws.snap(
"Error: Intersection of 8 and a number and a multiple of 3 results in an unsatisfiable type"
)

@@ -90,0 +94,0 @@ })

@@ -16,3 +16,3 @@ import { attest } from "@arktype/attest"

attest(t(e).out).equals(e)
attest(t({}).errors?.summary).snap("Must be an Error (was Object)")
attest(t({}).errors?.summary).snap("Must be an Error (was object)")
})

@@ -19,0 +19,0 @@ it("inherited", () => {

import type {
AnonymousRefinementKey,
AnonymousConstraintKey,
Morph,

@@ -50,4 +50,4 @@ distill,

type AnonymouslyRefined = {
[k in AnonymousRefinementKey]: { [_ in k]: true }
}[AnonymousRefinementKey]
[k in AnonymousConstraintKey]: { [_ in k]: true }
}[AnonymousConstraintKey]

@@ -54,0 +54,0 @@ type getHandledBranches<ctx extends MatchParserContext> = Exclude<

@@ -1,2 +0,2 @@

import type { AnonymousRefinementKey, distill, intersectConstrainables, is } from "@arktype/schema";
import type { AnonymousConstraintKey, distill, intersectConstrainables, is } from "@arktype/schema";
import type { ErrorMessage, UnknownUnion, isDisjoint, numericStringKeyOf, replaceKey, unionToTuple, valueOf } from "@arktype/util";

@@ -28,6 +28,6 @@ import type { Scope } from "./scope.js";

type AnonymouslyRefined = {
[k in AnonymousRefinementKey]: {
[k in AnonymousConstraintKey]: {
[_ in k]: true;
};
}[AnonymousRefinementKey];
}[AnonymousConstraintKey];
type getHandledBranches<ctx extends MatchParserContext> = Exclude<matcherInputs<ctx>, is<unknown, AnonymouslyRefined>>;

@@ -34,0 +34,0 @@ type getUnhandledBranches<ctx extends MatchParserContext> = Exclude<unknown extends ctx["exhaustiveOver"] ? UnknownUnion : ctx["exhaustiveOver"], getHandledBranches<ctx>>;

@@ -17,42 +17,38 @@ import { keywords, schema } from "@arktype/schema";

// https://discord.com/channels/957797212103016458/1103023445035462678/1182814502471860334
let hasSeenFirstKey = false;
const entries = stringAndSymbolicEntriesOf(def);
for (const entry of entries) {
const result = parseEntry(entry);
if (result.kind === "spread") {
if (hasSeenFirstKey) {
return throwParseError("Spread operator may only be used as the first key in an object");
}
const spreadNode = ctx.scope.parse(result.innerValue, ctx);
if (spreadNode.kind !== "intersection" ||
!spreadNode.extends(keywords.object)) {
return throwParseError(writeInvalidSpreadTypeMessage(printable(result.innerValue)));
}
// For each key on spreadNode, add it to our object.
// We filter out keys from the spreadNode that will be defined later on this same object
// because the currently parsed definition will overwrite them.
const requiredEntriesFromSpread = (spreadNode.props?.required ?? []).filter((e) => !entries.some(([k]) => k === e.key));
const optionalEntriesFromSpread = (spreadNode.props?.optional ?? []).filter((e) => !entries.some(([k]) => k === e.key));
required.push(...requiredEntriesFromSpread);
optional.push(...optionalEntriesFromSpread);
continue;
const parsedEntries = stringAndSymbolicEntriesOf(def).map(parseEntry);
if (parsedEntries[0].kind === "spread") {
// remove the spread entry so we can iterate over the remaining entries
// expecting non-spread entries
const spreadEntry = parsedEntries.shift();
const spreadNode = ctx.scope.parse(spreadEntry.innerValue, ctx);
if (spreadNode.kind !== "intersection" ||
!spreadNode.extends(keywords.object)) {
return throwParseError(writeInvalidSpreadTypeMessage(printable(spreadEntry.innerValue)));
}
ctx.path.push(`${typeof result.innerKey === "symbol"
? `[${printable(result.innerKey)}]`
: result.innerKey}`);
const valueNode = ctx.scope.parse(result.innerValue, ctx);
if (result.kind === "optional") {
optional.push({
key: result.innerKey,
value: valueNode
});
// TODO: move to props group merge in schema
// For each key on spreadNode, add it to our object.
// We filter out keys from the spreadNode that will be defined later on this same object
// because the currently parsed definition will overwrite them.
spreadNode.required?.forEach((spreadRequired) => !parsedEntries.some(({ innerKey }) => innerKey === spreadRequired.key) && required.push(spreadRequired));
spreadNode.optional?.forEach((spreadOptional) => !parsedEntries.some(({ innerKey }) => innerKey === spreadOptional.key) && optional.push(spreadOptional));
}
for (const entry of parsedEntries) {
if (entry.kind === "spread") {
return throwParseError("Spread operator may only be used as the first key in an object");
}
ctx.path.push(`${typeof entry.innerKey === "symbol"
? `[${printable(entry.innerKey)}]`
: entry.innerKey}`);
const value = ctx.scope.parse(entry.innerValue, ctx);
const inner = {
key: entry.innerKey,
value
};
if (entry.kind === "optional") {
optional.push(inner);
}
else {
required.push({
key: result.innerKey,
value: valueNode
});
required.push(inner);
}
ctx.path.pop();
hasSeenFirstKey ||= true;
}

@@ -59,0 +55,0 @@ return schema({

@@ -1,2 +0,2 @@

import type { DateLiteral, Refinements, RegexLiteral, distill, is } from "@arktype/schema";
import type { Constraints, DateLiteral, RegexLiteral, distill, inferIntersection, is } from "@arktype/schema";
import type { BigintLiteral, List, NumberLiteral, evaluate } from "@arktype/util";

@@ -8,8 +8,7 @@ import type { UnparsedScope, resolve, tryInferSubmoduleReference } from "../../scope.js";

import type { StringLiteral } from "../string/shift/operand/enclosed.js";
import type { inferIntersection } from "./intersections.js";
export type inferAstRoot<ast, $, args> = inferAst<ast, $, args, {}>;
export type inferAstBase<ast, $, args> = distill<inferAstRoot<ast, $, args>>;
export type inferAst<ast, $, args, refinements extends Refinements> = ast extends List ? inferExpression<ast, $, args, refinements> : inferTerminal<ast, $, args, refinements>;
export type inferAst<ast, $, args, refinements extends Constraints> = ast extends List ? inferExpression<ast, $, args, refinements> : inferTerminal<ast, $, args, refinements>;
export type GenericInstantiationAst<g extends GenericProps = GenericProps, argAsts extends unknown[] = unknown[]> = [g, "<>", argAsts];
export type inferExpression<ast extends List, $, args, refinements extends Refinements> = ast extends GenericInstantiationAst ? inferDefinition<ast[0]["definition"], ast[0]["$"] extends UnparsedScope ? $ : ast[0]["$"], {
export type inferExpression<ast extends List, $, args, refinements extends Constraints> = ast extends GenericInstantiationAst ? inferDefinition<ast[0]["definition"], ast[0]["$"] extends UnparsedScope ? $ : ast[0]["$"], {
[i in keyof ast[0]["parameters"] & `${number}` as ast[0]["parameters"][i]]: inferAst<ast[2][i & keyof ast[2]], $, args, refinements>;

@@ -29,3 +28,3 @@ }> : ast[1] extends "[]" ? inferAst<ast[0], $, args, refinements>[] : ast[1] extends "|" ? inferAst<ast[0], $, args, refinements> | inferAst<ast[2], $, args, refinements> : ast[1] extends "&" ? inferIntersection<inferAst<ast[0], $, args, refinements>, inferAst<ast[2], $, args, refinements>> : ast[1] extends Comparator ? ast[0] extends LimitLiteral ? inferAst<ast[2], $, args, refinements & {

export type InfixExpression<operator extends InfixOperator = InfixOperator, l = unknown, r = unknown> = [l, operator, r];
export type inferTerminal<token, $, args, refinements extends Refinements> = token extends keyof args | keyof $ ? {} extends refinements ? resolve<token, $, args> : is<resolve<token, $, args>, evaluate<refinements>> : token extends StringLiteral<infer text> ? text : token extends RegexLiteral ? is<string, evaluate<refinements & {
export type inferTerminal<token, $, args, refinements extends Constraints> = token extends keyof args | keyof $ ? {} extends refinements ? resolve<token, $, args> : is<resolve<token, $, args>, evaluate<refinements>> : token extends StringLiteral<infer text> ? text : token extends RegexLiteral ? is<string, evaluate<refinements & {
[_ in token]: true;

@@ -32,0 +31,0 @@ }>> : token extends DateLiteral ? is<Date, evaluate<refinements & {

@@ -11,5 +11,10 @@ import type { BaseMeta, Node } from "@arktype/schema";

};
export type EntryValueParseResult<kind extends ParsedValueKind = ParsedValueKind> = {
kind: kind;
innerValue: unknown;
};
export type EntryParseResult<kind extends ParsedKeyKind = ParsedKeyKind> = evaluate<KeyParseResult<kind> & {
innerValue: unknown;
}>;
type ParsedValueKind = "required" | "optional";
type ParsedKeyKind = "required" | "optional" | "indexed" | "spread";

@@ -23,2 +28,3 @@ type parsedEntry<result extends EntryParseResult> = result;

type DefinitionEntry = readonly [string | symbol, unknown];
export declare const parseEntryValue: (value: unknown) => EntryValueParseResult;
export declare const parseEntry: ([key, value]: DefinitionEntry) => EntryParseResult;

@@ -25,0 +31,0 @@ export type parseEntry<keyDef extends PropertyKey, valueDef> = parseKey<keyDef> extends infer keyParseResult extends KeyParseResult ? valueDef extends OptionalValue<infer innerValue> ? parsedEntry<{

import { Scanner } from "./string/shift/scanner.js";
const getInnerValue = (value) => {
export const parseEntryValue = (value) => {
if (typeof value === "string") {
if (value[value.length - 1] === "?") {
if (value.at(-1) === "?" && value.length > 1) {
return {

@@ -15,3 +15,4 @@ kind: "optional",

kind: "optional",
innerValue: getInnerValue(value[0]).innerValue
// allow redundant optionality like ["string?", "?"]
innerValue: parseEntryValue(value[0]).innerValue
};

@@ -37,3 +38,3 @@ }

: { innerKey: key, kind: "required" };
const valueParseResult = getInnerValue(value);
const valueParseResult = parseEntryValue(value);
return {

@@ -40,0 +41,0 @@ innerKey: keyParseResult.innerKey,

@@ -1,13 +0,11 @@

import { type BaseMeta, type Morph, type Out, type Predicate, type TypeNode, type extractIn, type extractOut, type inferMorphOut, type inferNarrow } from "@arktype/schema";
import { type Constructor, type Domain, type List, type conform, type evaluate, type isAny } from "@arktype/util";
import { type BaseMeta, type Morph, type Out, type Predicate, type TypeNode, type extractIn, type extractOut, type inferIntersection, type inferMorphOut, type inferNarrow } from "@arktype/schema";
import { type Constructor, type Domain, type ErrorMessage, type List, type conform, type evaluate, type isAny } from "@arktype/util";
import type { ParseContext } from "../scope.js";
import type { inferDefinition, validateDefinition } from "./definition.js";
import type { inferIntersection } from "./semantic/intersections.js";
import type { InfixOperator, PostfixExpression } from "./semantic/semantic.js";
import { parseEntry, type EntryParseResult, type validateObjectValue } from "./shared.js";
import { type EntryParseResult, type parseEntry, type validateObjectValue } from "./shared.js";
import { writeMissingRightOperandMessage } from "./string/shift/operand/unenclosed.js";
import type { BaseCompletions } from "./string/string.js";
export declare const parseTuple: (def: List, ctx: ParseContext) => TypeNode;
export declare const parseTupleLiteral: (def: List, ctx: ParseContext) => TypeNode;
export declare const maybeParseTupleExpression: (def: List, ctx: ParseContext) => TypeNode | undefined;
export declare const parseTuple: (def: readonly unknown[], ctx: ParseContext) => TypeNode;
export declare const parseTupleLiteral: (def: readonly unknown[], ctx: ParseContext) => TypeNode;
type InfixExpression = readonly [unknown, InfixOperator, ...unknown[]];

@@ -20,10 +18,13 @@ export type validateTuple<def extends List, $, args> = def extends IndexZeroExpression ? validatePrefixExpression<def, $, args> : def extends PostfixExpression ? validatePostfixExpression<def, $, args> : def extends InfixExpression ? validateInfixExpression<def, $, args> : def extends readonly ["", ...unknown[]] | readonly [unknown, "", ...unknown[]] ? readonly [

...result,
validateTupleElement<head, tail, $, args>
validateTupleElement<head, result, $, args>
]> : result;
type validateTupleElement<head, tail, $, args> = head extends variadicExpression<infer operand> ? validateDefinition<operand, $, args> extends infer syntacticResult ? syntacticResult extends operand ? semanticallyValidateRestElement<operand, $, args> extends infer semanticResult ? semanticResult extends operand ? tail extends [] ? head : prematureRestMessage : semanticResult : never : syntacticResult : never : validateObjectValue<head, $, args>;
type semanticallyValidateRestElement<operand, $, args> = inferDefinition<operand, $, args> extends infer result ? result extends never ? writeNonArrayRestMessage<operand> : isAny<result> extends true ? writeNonArrayRestMessage<operand> : result extends readonly unknown[] ? operand : writeNonArrayRestMessage<operand> : never;
export declare const writeNonArrayRestMessage: <operand>(operand: operand) => `Rest element ${operand extends string ? `'${operand}'` : ""} must be an array`;
type writeNonArrayRestMessage<operand> = `Rest element ${operand extends string ? `'${operand}'` : ""} must be an array`;
type validateTupleElement<head, result extends unknown[], $, args> = head extends variadicExpression<infer operand> ? validateDefinition<operand, $, args> extends infer syntacticResult ? syntacticResult extends operand ? semanticallyValidateRestElement<operand, $, args> extends infer semanticResult ? semanticResult extends operand ? Extract<result[number], variadicExpression> extends never ? head : ErrorMessage<multipleVariadicMessage> : semanticResult : never : syntacticResult : never : validateObjectValue<head, $, args>;
type semanticallyValidateRestElement<operand, $, args> = inferDefinition<operand, $, args> extends infer result ? result extends never ? writeNonArraySpreadMessage<operand> : isAny<result> extends true ? writeNonArraySpreadMessage<operand> : result extends readonly unknown[] ? operand : writeNonArraySpreadMessage<operand> : never;
export declare const writeNonArraySpreadMessage: <operand extends string | TypeNode>(operand: operand) => `Spread element must be an array${operand extends string ? `(was ${operand})` : ""}`;
type writeNonArraySpreadMessage<operand> = `Spread element must be an array${operand extends string ? `(was ${operand})` : ""}`;
export declare const multipleVariadicMesage = "A tuple may have at most one variadic element";
type prematureRestMessage = typeof multipleVariadicMesage;
type multipleVariadicMessage = typeof multipleVariadicMesage;
export declare const requiredPostOptionalMessage = "A required element may not follow an optional element";
export declare const optionalPostVariadicMessage = "An optional element may not follow a variadic element";
export declare const spreadOptionalMessage = "A spread element cannot be optional";
type inferTupleLiteral<def extends List, $, args, result extends unknown[] = []> = def extends readonly [infer head, ...infer tail] ? parseEntry<result["length"], head extends variadicExpression<infer operand> ? operand : head> extends infer entryParseResult extends EntryParseResult ? inferDefinition<entryParseResult["innerValue"], $, args> extends infer element ? head extends variadicExpression ? element extends readonly unknown[] ? inferTupleLiteral<tail, $, args, [...result, ...element]> : never : inferTupleLiteral<tail, $, args, entryParseResult["kind"] extends "optional" ? [...result, element?] : [...result, element]> : never : never : result;

@@ -30,0 +31,0 @@ type variadicExpression<operandDef = unknown> = variadicStringExpression<operandDef & string> | variadicTupleExpression<operandDef>;

@@ -1,65 +0,104 @@

import { keywords, schema } from "@arktype/schema";
import { isArray, objectKindOrDomainOf, printable, throwParseError } from "@arktype/util";
import { keywords, makeRootAndArrayPropertiesMutable, schema } from "@arktype/schema";
import { append, isArray, objectKindOrDomainOf, printable, throwParseError } from "@arktype/util";
import { writeUnsatisfiableExpressionError } from "./semantic/validate.js";
import { configureShallowDescendants, parseEntry } from "./shared.js";
import { configureShallowDescendants, parseEntryValue } from "./shared.js";
import { writeMissingRightOperandMessage } from "./string/shift/operand/unenclosed.js";
export const parseTuple = (def, ctx) => maybeParseTupleExpression(def, ctx) ?? parseTupleLiteral(def, ctx);
export const parseTupleLiteral = (def, ctx) => {
const props = [];
let variadicIndex;
let sequences = [{}];
for (let i = 0; i < def.length; i++) {
let elementDef = def[i];
ctx.path.push(`${i}`);
if (typeof elementDef === "string" && elementDef.startsWith("...")) {
elementDef = elementDef.slice(3);
if (variadicIndex !== undefined) {
return throwParseError(multipleVariadicMesage);
const parsedElementDef = parseSpreadable(def[i]);
const element = ctx.scope.parse(parsedElementDef.innerValue, ctx);
if (parsedElementDef.spread) {
if (parsedElementDef.kind === "optional") {
return throwParseError(spreadOptionalMessage);
}
variadicIndex = i;
}
else if (isArray(elementDef) &&
elementDef.length === 2 &&
elementDef[0] === "...") {
elementDef = elementDef[1];
if (variadicIndex !== undefined) {
return throwParseError(multipleVariadicMesage);
if (!element.extends(keywords.Array)) {
return throwParseError(writeNonArraySpreadMessage(element));
}
variadicIndex = i;
// a spread must be distributed over branches e.g.:
// def: [string, ...(number[] | [true, false])]
// nodes: [string, ...number[]] | [string, true, false]
sequences = sequences.flatMap((base) =>
// since appendElement mutates base, we have to shallow-ish clone it for each branch
element.branches.map((branch) => appendSpreadBranch(makeRootAndArrayPropertiesMutable(base), branch)));
}
const parsedEntry = parseEntry([`${i}`, elementDef]);
const value = ctx.scope.parse(parsedEntry.innerValue, ctx);
if (variadicIndex === i) {
if (!value.extends(keywords.Array)) {
return throwParseError(writeNonArrayRestMessage(elementDef));
}
// TODO: Fix builtins.arrayIndexTypeNode()
const elementType = value.getPath();
// TODO: first variadic i
props.push({ key: keywords.number, value: elementType });
}
else {
props.push({
key: {
name: `${i}`,
prerequisite: false,
optional: parsedEntry.kind === "optional"
},
value
});
sequences = sequences.map((base) => appendElement(base, parsedElementDef.kind === "optional" ? "optional" : "required", element));
}
ctx.path.pop();
}
if (variadicIndex === undefined) {
props.push({
key: {
name: "length",
prerequisite: true,
optional: false
},
value: schema({ unit: def.length })
});
return schema(...sequences.map((sequence) => ({
basis: Array,
sequence
})));
};
const appendElement = (base, kind, element) => {
switch (kind) {
case "required":
if (base.optionals)
// e.g. [string?, number]
return throwParseError(requiredPostOptionalMessage);
if (base.variadic) {
// e.g. [...string[], number]
base.postfix = append(base.postfix, element);
}
else {
// e.g. [string, number]
base.prefix = append(base.prefix, element);
}
return base;
case "optional":
if (base.variadic)
// e.g. [...string[], number?]
return throwParseError(optionalPostVariadicMessage);
// e.g. [string, number?]
base.optionals = append(base.optionals, element);
return base;
case "variadic":
// e.g. [...string[], number, ...string[]]
if (base.postfix)
throwParseError(multipleVariadicMesage);
if (base.variadic) {
if (!base.variadic.equals(element)) {
// e.g. [...string[], ...number[]]
throwParseError(multipleVariadicMesage);
}
// e.g. [...string[], ...string[]]
// do nothing, second spread doesn't change the type
}
else {
// e.g. [string, ...number[]]
base.variadic = element;
}
return base;
}
return schema(Array);
};
export const maybeParseTupleExpression = (def, ctx) => {
const appendSpreadBranch = (base, branch) => {
const spread = branch.firstReferenceOfKind("sequence");
if (!spread) {
// the only array with no sequence reference is unknown[]
return appendElement(base, "variadic", keywords.unknown);
}
spread.prefix.forEach((node) => appendElement(base, "required", node));
spread.optionals.forEach((node) => appendElement(base, "optional", node));
spread.variadic && appendElement(base, "variadic", spread.variadic);
spread.postfix.forEach((node) => appendElement(base, "required", node));
return base;
};
const parseSpreadable = (elementDef) => {
if (typeof elementDef === "string" && elementDef.startsWith("...")) {
// variadic string definition like "...string[]"
return { ...parseEntryValue(elementDef.slice(3)), spread: true };
}
if (isArray(elementDef) &&
elementDef.length === 2 &&
elementDef[0] === "...") {
// variadic tuple expression like ["...", { a: "1" }]
return { ...parseEntryValue(elementDef[1]), spread: true };
}
return parseEntryValue(elementDef);
};
const maybeParseTupleExpression = (def, ctx) => {
const tupleExpressionResult = isIndexOneExpression(def)

@@ -78,4 +117,7 @@ ? indexOneParsers[def[1]](def, ctx)

};
export const writeNonArrayRestMessage = (operand) => `Rest element ${typeof operand === "string" ? `'${operand}'` : ""} must be an array`;
export const writeNonArraySpreadMessage = (operand) => `Spread element must be an array (was ${operand})`;
export const multipleVariadicMesage = `A tuple may have at most one variadic element`;
export const requiredPostOptionalMessage = `A required element may not follow an optional element`;
export const optionalPostVariadicMessage = `An optional element may not follow a variadic element`;
export const spreadOptionalMessage = `A spread element cannot be optional`;
export const parseKeyOfTuple = (def, ctx) => ctx.scope.parse(def[1], ctx).keyof();

@@ -82,0 +124,0 @@ const parseBranchTuple = (def, ctx) => {

import { BaseType, keywords } from "@arktype/schema";
import { domainOf, hasDomain, isThunk, map, throwParseError } from "@arktype/util";
import { domainOf, hasDomain, isThunk, morph, throwParseError } from "@arktype/util";
import { createMatchParser } from "./match.js";

@@ -149,3 +149,3 @@ import { parseObject, writeBadDefinitionTypeMessage } from "./parser/definition.js";

import(...names) {
return addArkKind(map(this.export(...names), (alias, value) => [
return addArkKind(morph(this.export(...names), (alias, value) => [
`#${alias}`,

@@ -187,3 +187,6 @@ value

const namesToExport = names.length ? names : this.exportedNames;
return addArkKind(map(namesToExport, (_, name) => [name, this.exportCache[name]]), "module");
return addArkKind(morph(namesToExport, (_, name) => [
name,
this.exportCache[name]
]), "module");
}

@@ -197,3 +200,3 @@ }

const innerResolutions = resolutionsOfModule(v);
const prefixedResolutions = map(innerResolutions, (innerK, innerV) => [
const prefixedResolutions = morph(innerResolutions, (innerK, innerV) => [
`${k}.${innerK}`,

@@ -200,0 +203,0 @@ innerV

@@ -1,6 +0,5 @@

import { inferred, type ArkResult, type BaseMeta, type Morph, type Out, type Predicate, type TypeNode, type distill, type extractIn, type extractOut, type includesMorphs, type inferMorphOut, type inferNarrow } from "@arktype/schema";
import { inferred, type ArkResult, type BaseMeta, type Morph, type Out, type Predicate, type TypeNode, type distill, type extractIn, type extractOut, type includesMorphs, type inferIntersection, type inferMorphOut, type inferNarrow } from "@arktype/schema";
import { Callable, type Constructor, type Json, type conform } from "@arktype/util";
import type { inferDefinition, validateDeclared, validateDefinition } from "./parser/definition.js";
import { parseGenericParams, type GenericParamsParseError } from "./parser/generic.js";
import type { inferIntersection } from "./parser/semantic/intersections.js";
import type { IndexOneOperator, IndexZeroOperator, TupleInfixOperator } from "./parser/tuple.js";

@@ -7,0 +6,0 @@ import type { Scope, bindThis } from "./scope.js";

import { inferred, keywords } from "@arktype/schema";
import { Callable, map } from "@arktype/util";
import { Callable, morph } from "@arktype/util";
import { parseGenericParams } from "./parser/generic.js";

@@ -118,3 +118,3 @@ import { configureShallowDescendants } from "./parser/shared.js";

baseName: "generic",
args: map(g.parameters, (_, name) => [name, keywords.unknown])
args: morph(g.parameters, (_, name) => [name, keywords.unknown])
});

@@ -125,3 +125,3 @@ return g;

return Object.assign((...args) => {
const argNodes = map(parameters, (i, param) => [
const argNodes = morph(parameters, (i, param) => [
param,

@@ -128,0 +128,0 @@ parseTypeRoot(args[i], scope)

{
"name": "arktype",
"description": "TypeScript's 1:1 validator, optimized from editor to runtime",
"version": "2.0.0-dev.2",
"version": "2.0.0-dev.3",
"license": "MIT",

@@ -37,5 +37,5 @@ "author": {

"dependencies": {
"@arktype/util": "0.0.20",
"@arktype/schema": "0.0.4"
"@arktype/util": "0.0.22",
"@arktype/schema": "workspace:*"
}
}

@@ -38,41 +38,42 @@ import { keywords, schema, type Inner, type TypeNode } from "@arktype/schema"

// https://discord.com/channels/957797212103016458/1103023445035462678/1182814502471860334
let hasSeenFirstKey = false
const entries = stringAndSymbolicEntriesOf(def)
for (const entry of entries) {
const result = parseEntry(entry)
const parsedEntries = stringAndSymbolicEntriesOf(def).map(parseEntry)
if (parsedEntries[0].kind === "spread") {
// remove the spread entry so we can iterate over the remaining entries
// expecting non-spread entries
const spreadEntry = parsedEntries.shift()!
const spreadNode = ctx.scope.parse(spreadEntry.innerValue, ctx)
if (result.kind === "spread") {
if (hasSeenFirstKey) {
return throwParseError(
"Spread operator may only be used as the first key in an object"
)
}
if (
spreadNode.kind !== "intersection" ||
!spreadNode.extends(keywords.object)
) {
return throwParseError(
writeInvalidSpreadTypeMessage(printable(spreadEntry.innerValue))
)
}
const spreadNode = ctx.scope.parse(result.innerValue, ctx)
// TODO: move to props group merge in schema
// For each key on spreadNode, add it to our object.
// We filter out keys from the spreadNode that will be defined later on this same object
// because the currently parsed definition will overwrite them.
spreadNode.required?.forEach(
(spreadRequired) =>
!parsedEntries.some(
({ innerKey }) => innerKey === spreadRequired.key
) && required.push(spreadRequired)
)
if (
spreadNode.kind !== "intersection" ||
!spreadNode.extends(keywords.object)
) {
return throwParseError(
writeInvalidSpreadTypeMessage(printable(result.innerValue))
)
}
// For each key on spreadNode, add it to our object.
// We filter out keys from the spreadNode that will be defined later on this same object
// because the currently parsed definition will overwrite them.
const requiredEntriesFromSpread = (
spreadNode.props?.required ?? []
).filter((e) => !entries.some(([k]) => k === e.key))
const optionalEntriesFromSpread = (
spreadNode.props?.optional ?? []
).filter((e) => !entries.some(([k]) => k === e.key))
required.push(...requiredEntriesFromSpread)
optional.push(...optionalEntriesFromSpread)
continue
spreadNode.optional?.forEach(
(spreadOptional) =>
!parsedEntries.some(
({ innerKey }) => innerKey === spreadOptional.key
) && optional.push(spreadOptional)
)
}
for (const entry of parsedEntries) {
if (entry.kind === "spread") {
return throwParseError(
"Spread operator may only be used as the first key in an object"
)
}

@@ -82,23 +83,18 @@

`${
typeof result.innerKey === "symbol"
? `[${printable(result.innerKey)}]`
: result.innerKey
typeof entry.innerKey === "symbol"
? `[${printable(entry.innerKey)}]`
: entry.innerKey
}`
)
const valueNode = ctx.scope.parse(result.innerValue, ctx)
if (result.kind === "optional") {
optional.push({
key: result.innerKey,
value: valueNode
})
const value = ctx.scope.parse(entry.innerValue, ctx)
const inner: Inner<"required" | "optional"> = {
key: entry.innerKey,
value
}
if (entry.kind === "optional") {
optional.push(inner)
} else {
required.push({
key: result.innerKey,
value: valueNode
})
required.push(inner)
}
ctx.path.pop()
hasSeenFirstKey ||= true
}

@@ -105,0 +101,0 @@

import type {
Constraints,
DateLiteral,
Refinements,
RegexLiteral,
distill,
inferIntersection,
is

@@ -27,3 +28,2 @@ } from "@arktype/schema"

import type { StringLiteral } from "../string/shift/operand/enclosed.js"
import type { inferIntersection } from "./intersections.js"

@@ -38,3 +38,3 @@ export type inferAstRoot<ast, $, args> = inferAst<ast, $, args, {}>

args,
refinements extends Refinements
refinements extends Constraints
> = ast extends List

@@ -53,3 +53,3 @@ ? inferExpression<ast, $, args, refinements>

args,
refinements extends Refinements
refinements extends Constraints
> = ast extends GenericInstantiationAst

@@ -128,3 +128,3 @@ ? inferDefinition<

args,
refinements extends Refinements
refinements extends Constraints
> = token extends keyof args | keyof $

@@ -131,0 +131,0 @@ ? {} extends refinements

@@ -1,2 +0,2 @@

import type { Refinements } from "@arktype/schema"
import type { Constraints } from "@arktype/schema"
import type {

@@ -3,0 +3,0 @@ BigintLiteral,

@@ -15,3 +15,5 @@ import type { BaseMeta, Node } from "@arktype/schema"

type ValueParseResult<kind extends ParsedValueKind = ParsedValueKind> = {
export type EntryValueParseResult<
kind extends ParsedValueKind = ParsedValueKind
> = {
kind: kind

@@ -55,5 +57,5 @@ innerValue: unknown

const getInnerValue = (value: unknown): ValueParseResult => {
export const parseEntryValue = (value: unknown): EntryValueParseResult => {
if (typeof value === "string") {
if (value[value.length - 1] === "?") {
if (value.at(-1) === "?" && value.length > 1) {
return {

@@ -68,3 +70,4 @@ kind: "optional",

kind: "optional",
innerValue: getInnerValue(value[0]).innerValue
// allow redundant optionality like ["string?", "?"]
innerValue: parseEntryValue(value[0]).innerValue
}

@@ -93,3 +96,3 @@ }

: { innerKey: key, kind: "required" }
const valueParseResult = getInnerValue(value)
const valueParseResult = parseEntryValue(value)
return {

@@ -96,0 +99,0 @@ innerKey: keyParseResult.innerKey,

import {
keywords,
makeRootAndArrayPropertiesMutable,
schema,

@@ -7,2 +8,4 @@ type BaseMeta,

type MorphChildKind,
type MutableInner,
type Node,
type Out,

@@ -12,4 +15,6 @@ type Predicate,

type TypeNode,
type UnionChildKind,
type extractIn,
type extractOut,
type inferIntersection,
type inferMorphOut,

@@ -19,2 +24,3 @@ type inferNarrow

import {
append,
isArray,

@@ -27,2 +33,3 @@ objectKindOrDomainOf,

type Domain,
type ErrorMessage,
type List,

@@ -35,3 +42,2 @@ type conform,

import type { inferDefinition, validateDefinition } from "./definition.js"
import type { inferIntersection } from "./semantic/intersections.js"
import type { InfixOperator, PostfixExpression } from "./semantic/semantic.js"

@@ -41,4 +47,6 @@ import { writeUnsatisfiableExpressionError } from "./semantic/validate.js"

configureShallowDescendants,
parseEntry,
parseEntryValue,
type EntryParseResult,
type EntryValueParseResult,
type parseEntry,
type validateObjectValue

@@ -49,64 +57,127 @@ } from "./shared.js"

export const parseTuple = (def: List, ctx: ParseContext) =>
export const parseTuple = (def: readonly unknown[], ctx: ParseContext) =>
maybeParseTupleExpression(def, ctx) ?? parseTupleLiteral(def, ctx)
export const parseTupleLiteral = (def: List, ctx: ParseContext): TypeNode => {
const props: unknown[] = []
let variadicIndex: number | undefined
export const parseTupleLiteral = (
def: readonly unknown[],
ctx: ParseContext
): TypeNode => {
let sequences: MutableInner<"sequence">[] = [{}]
for (let i = 0; i < def.length; i++) {
let elementDef = def[i]
ctx.path.push(`${i}`)
if (typeof elementDef === "string" && elementDef.startsWith("...")) {
elementDef = elementDef.slice(3)
if (variadicIndex !== undefined) {
return throwParseError(multipleVariadicMesage)
const parsedElementDef = parseSpreadable(def[i])
const element = ctx.scope.parse(parsedElementDef.innerValue, ctx)
if (parsedElementDef.spread) {
if (parsedElementDef.kind === "optional") {
return throwParseError(spreadOptionalMessage)
}
variadicIndex = i
} else if (
isArray(elementDef) &&
elementDef.length === 2 &&
elementDef[0] === "..."
) {
elementDef = elementDef[1]
if (variadicIndex !== undefined) {
return throwParseError(multipleVariadicMesage)
if (!element.extends(keywords.Array)) {
return throwParseError(writeNonArraySpreadMessage(element))
}
variadicIndex = i
}
const parsedEntry = parseEntry([`${i}`, elementDef])
const value = ctx.scope.parse(parsedEntry.innerValue, ctx)
if (variadicIndex === i) {
if (!value.extends(keywords.Array)) {
return throwParseError(writeNonArrayRestMessage(elementDef))
}
// TODO: Fix builtins.arrayIndexTypeNode()
const elementType = value.getPath()
// TODO: first variadic i
props.push({ key: keywords.number, value: elementType })
// a spread must be distributed over branches e.g.:
// def: [string, ...(number[] | [true, false])]
// nodes: [string, ...number[]] | [string, true, false]
sequences = sequences.flatMap((base) =>
// since appendElement mutates base, we have to shallow-ish clone it for each branch
element.branches.map((branch) =>
appendSpreadBranch(makeRootAndArrayPropertiesMutable(base), branch)
)
)
} else {
props.push({
key: {
name: `${i}`,
prerequisite: false,
optional: parsedEntry.kind === "optional"
},
value
})
sequences = sequences.map((base) =>
appendElement(
base,
parsedElementDef.kind === "optional" ? "optional" : "required",
element
)
)
}
ctx.path.pop()
}
if (variadicIndex === undefined) {
props.push({
key: {
name: "length",
prerequisite: true,
optional: false
},
value: schema({ unit: def.length })
})
return schema(
...sequences.map((sequence) => ({
basis: Array,
sequence
}))
)
}
const appendElement = (
base: MutableInner<"sequence">,
kind: "optional" | "required" | "variadic",
element: TypeNode
): MutableInner<"sequence"> => {
switch (kind) {
case "required":
if (base.optionals)
// e.g. [string?, number]
return throwParseError(requiredPostOptionalMessage)
if (base.variadic) {
// e.g. [...string[], number]
base.postfix = append(base.postfix, element)
} else {
// e.g. [string, number]
base.prefix = append(base.prefix, element)
}
return base
case "optional":
if (base.variadic)
// e.g. [...string[], number?]
return throwParseError(optionalPostVariadicMessage)
// e.g. [string, number?]
base.optionals = append(base.optionals, element)
return base
case "variadic":
// e.g. [...string[], number, ...string[]]
if (base.postfix) throwParseError(multipleVariadicMesage)
if (base.variadic) {
if (!base.variadic.equals(element)) {
// e.g. [...string[], ...number[]]
throwParseError(multipleVariadicMesage)
}
// e.g. [...string[], ...string[]]
// do nothing, second spread doesn't change the type
} else {
// e.g. [string, ...number[]]
base.variadic = element
}
return base
}
return schema(Array)
}
export const maybeParseTupleExpression = (
const appendSpreadBranch = (
base: MutableInner<"sequence">,
branch: Node<UnionChildKind>
): MutableInner<"sequence"> => {
const spread = branch.firstReferenceOfKind("sequence")
if (!spread) {
// the only array with no sequence reference is unknown[]
return appendElement(base, "variadic", keywords.unknown)
}
spread.prefix.forEach((node) => appendElement(base, "required", node))
spread.optionals.forEach((node) => appendElement(base, "optional", node))
spread.variadic && appendElement(base, "variadic", spread.variadic)
spread.postfix.forEach((node) => appendElement(base, "required", node))
return base
}
type TupleElementParseResult = EntryValueParseResult & { spread?: true }
const parseSpreadable = (elementDef: unknown): TupleElementParseResult => {
if (typeof elementDef === "string" && elementDef.startsWith("...")) {
// variadic string definition like "...string[]"
return { ...parseEntryValue(elementDef.slice(3)), spread: true }
}
if (
isArray(elementDef) &&
elementDef.length === 2 &&
elementDef[0] === "..."
) {
// variadic tuple expression like ["...", { a: "1" }]
return { ...parseEntryValue(elementDef[1]), spread: true }
}
return parseEntryValue(elementDef)
}
const maybeParseTupleExpression = (
def: List,

@@ -167,24 +238,28 @@ ctx: ParseContext

args,
[...result, validateTupleElement<head, tail, $, args>]
[...result, validateTupleElement<head, result, $, args>]
>
: result
type validateTupleElement<head, tail, $, args> =
head extends variadicExpression<infer operand>
? validateDefinition<operand, $, args> extends infer syntacticResult
? syntacticResult extends operand
? semanticallyValidateRestElement<
operand,
$,
args
> extends infer semanticResult
? semanticResult extends operand
? tail extends []
? head
: prematureRestMessage
: semanticResult
: never
: syntacticResult
: never
: validateObjectValue<head, $, args>
type validateTupleElement<
head,
result extends unknown[],
$,
args
> = head extends variadicExpression<infer operand>
? validateDefinition<operand, $, args> extends infer syntacticResult
? syntacticResult extends operand
? semanticallyValidateRestElement<
operand,
$,
args
> extends infer semanticResult
? semanticResult extends operand
? Extract<result[number], variadicExpression> extends never
? head
: ErrorMessage<multipleVariadicMessage>
: semanticResult
: never
: syntacticResult
: never
: validateObjectValue<head, $, args>

@@ -197,23 +272,36 @@ type semanticallyValidateRestElement<operand, $, args> = inferDefinition<

? result extends never
? writeNonArrayRestMessage<operand>
? writeNonArraySpreadMessage<operand>
: isAny<result> extends true
? writeNonArrayRestMessage<operand>
? writeNonArraySpreadMessage<operand>
: result extends readonly unknown[]
? operand
: writeNonArrayRestMessage<operand>
: writeNonArraySpreadMessage<operand>
: never
export const writeNonArrayRestMessage = <operand>(operand: operand) =>
`Rest element ${
typeof operand === "string" ? `'${operand}'` : ""
} must be an array` as writeNonArrayRestMessage<operand>
export const writeNonArraySpreadMessage = <operand extends string | TypeNode>(
operand: operand
) =>
`Spread element must be an array (was ${operand})` as writeNonArraySpreadMessage<operand>
type writeNonArrayRestMessage<operand> = `Rest element ${operand extends string
? `'${operand}'`
: ""} must be an array`
type writeNonArraySpreadMessage<operand> =
`Spread element must be an array${operand extends string
? `(was ${operand})`
: ""}`
export const multipleVariadicMesage = `A tuple may have at most one variadic element`
type prematureRestMessage = typeof multipleVariadicMesage
type multipleVariadicMessage = typeof multipleVariadicMesage
export const requiredPostOptionalMessage = `A required element may not follow an optional element`
type requiredPostOptionalMessage = typeof requiredPostOptionalMessage
export const optionalPostVariadicMessage = `An optional element may not follow a variadic element`
type optionalPostVariadicMessage = typeof optionalPostVariadicMessage
export const spreadOptionalMessage = `A spread element cannot be optional`
type spreadOptionalMessage = typeof optionalPostVariadicMessage
type inferTupleLiteral<

@@ -220,0 +308,0 @@ def extends List,

@@ -13,3 +13,3 @@ import {

isThunk,
map,
morph,
throwParseError,

@@ -403,3 +403,3 @@ type Dict,

return addArkKind(
map(this.export(...names) as Dict, (alias, value) => [
morph(this.export(...names) as Dict, (alias, value) => [
`#${alias}`,

@@ -449,3 +449,6 @@ value

return addArkKind(
map(namesToExport, (_, name) => [name, this.exportCache![name]]) as never,
morph(namesToExport, (_, name) => [
name,
this.exportCache![name]
]) as never,
"module"

@@ -464,3 +467,3 @@ ) as never

const innerResolutions = resolutionsOfModule(v as never)
const prefixedResolutions = map(innerResolutions, (innerK, innerV) => [
const prefixedResolutions = morph(innerResolutions, (innerK, innerV) => [
`${k}.${innerK}`,

@@ -467,0 +470,0 @@ innerV

@@ -14,2 +14,3 @@ import {

type includesMorphs,
type inferIntersection,
type inferMorphOut,

@@ -20,3 +21,3 @@ type inferNarrow

Callable,
map,
morph,
type Constructor,

@@ -35,3 +36,2 @@ type Json,

} from "./parser/generic.js"
import type { inferIntersection } from "./parser/semantic/intersections.js"
import { configureShallowDescendants } from "./parser/shared.js"

@@ -286,3 +286,3 @@ import type {

baseName: "generic",
args: map(g.parameters, (_, name) => [name, keywords.unknown])
args: morph(g.parameters, (_, name) => [name, keywords.unknown])
}

@@ -300,3 +300,3 @@ )

(...args: unknown[]) => {
const argNodes = map(parameters, (i, param) => [
const argNodes = morph(parameters, (i, param) => [
param,

@@ -303,0 +303,0 @@ parseTypeRoot(args[i], scope)

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc