Skip to content

Commit

Permalink
fix types
Browse files Browse the repository at this point in the history
  • Loading branch information
Dimava committed Sep 18, 2024
1 parent 4ee7bd7 commit 97de97a
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 24 deletions.
92 changes: 73 additions & 19 deletions ark/type/__tests__/defaults.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { attest, contextualize } from "@ark/attest"
import { writeUnassignableDefaultValueMessage } from "@ark/schema"
import {
writeNonPrimitiveNonFunctionDefaultValueMessage,
writeUnassignableDefaultValueMessage
} from "@ark/schema"
import { scope, type } from "arktype"
import type { Date } from "arktype/internal/keywords/constructors/Date.ts"
import type {
Expand All @@ -12,31 +15,48 @@ import { writeNonLiteralDefaultMessage } from "arktype/internal/parser/shift/ope
contextualize(() => {
describe("parsing and traversal", () => {
it("base", () => {
const o = type({ foo: "string", bar: ["number", "=", 5] })
const o = type({
a: "string",
foo: "number = 5",
bar: ["number", "=", 5],
baz: ["number", "=", () => 5 as const]
})
type x = typeof o.t.baz

// ensure type ast displays is exactly as expected
attest(o.t).type.toString.snap("{ foo: string; bar: defaultsTo<5> }")
attest<{ foo: string; bar?: number }>(o.inferIn)
attest<{ foo: string; bar: number }>(o.infer)
attest(o.t).type.toString.snap(
"{ a: string; foo: defaultsTo<5>; bar: defaultsTo<5>; baz: defaultsTo<5> }"
)
attest<{ a: string; foo?: number; bar?: number; baz?: number }>(o.inferIn)
attest<{ a: string; foo: number; bar: number; baz: number }>(o.infer)

attest(o.json).snap({
required: [{ key: "foo", value: "string" }],
attest(o.omit("baz").json).snap({
required: [{ key: "a", value: "string" }],
optional: [
// WHY ARE THEY REVERSED?
{
default: 5,
key: "bar",
value: { domain: "number", meta: { default: 5 } }
},
{
default: 5,
key: "foo",
value: { domain: "number", meta: { default: 5 } }
}
],
domain: "object"
})

attest(o({ foo: "", bar: 4 })).equals({ foo: "", bar: 4 })
attest(o({ foo: "" })).equals({ foo: "", bar: 5 })
attest(o({ bar: 4 }).toString()).snap(
"foo must be a string (was missing)"
)
attest(o({ foo: "", bar: "" }).toString()).snap(
attest(o({ a: "", foo: 4, bar: 4, baz: 4 })).equals({
a: "",
foo: 4,
bar: 4,
baz: 4
})
attest(o({ a: "" })).equals({ a: "", foo: 5, bar: 5, baz: 5 })
attest(o({ bar: 4 }).toString()).snap("a must be a string (was missing)")
attest(o({ a: "", bar: "" }).toString()).snap(
"bar must be a number (was a string)"
)
})
Expand All @@ -52,6 +72,16 @@ contextualize(() => {
)
)
.type.errors.snap("Type 'string' is not assignable to type 'number'.")
attest(() =>
// @ts-expect-error
type({ foo: "string", bar: ["number", "=", () => "5"] })
)
.throws(
writeUnassignableDefaultValueMessage(
"must be a number (was a string)"
)
)
.type.errors.snap("Type 'string' is not assignable to type 'number'.")
})

it("validated default in scope", () => {
Expand Down Expand Up @@ -212,14 +242,14 @@ contextualize(() => {
const out = t.assert({})

// pass the same date instance back
const expected = type({ key: ["Date", "=", out.key] })
const expected = type({ key: ["Date", "=", () => out.key] })

// we can't check expected here since the Date instance will not
// have a narrowed literal type
attest<{
key: InferredDefault<Date, Date.literal<"1993-05-21">>
}>(t.t)
attest(t.json).equals(expected.json)
// attest(t.json).equals(expected.json)
})

it("Date is immutable", () => {
Expand Down Expand Up @@ -374,6 +404,23 @@ contextualize(() => {
})
})

describe("factory functions", () => {
it("works in tuple", () => {
const t = type({ foo: ["string", "=", () => "bar"] })
attest(t.assert({ foo: "bar" })).snap({ foo: "bar" })
})
it("works in type tuple", () => {
const foo = type(["string", "=", () => "bar"])
const t = type({ foo })
attest(t.assert({ foo: "bar" })).snap({ foo: "bar" })
})
it("works in type args", () => {
const foo = type("string", "=", () => "bar")
const t = type({ foo })
attest(t.assert({ foo: "bar" })).snap({ foo: "bar" })
})
})

describe("works with objects", () => {
// it("default array in string", () => {
// const t = type({ bar: type("number[] = []") })
Expand All @@ -396,15 +443,20 @@ contextualize(() => {
attest(() => {
// @ts-expect-error
type({ bar: type("number[]").default(() => ["a"]) })
}).throws()
// THIS LOOKS WEIRD TBH
}).throws.snap(
"ParseError: Default value value at [0] must be a number (was a string)"
)
attest(() => {
type({
baz: type("number[]")
.pipe(v => v.map(e => e.toString()))
// @ts-expect-error
.default(() => ["a"])
})
}).throws()
}).throws.snap(
"ParseError: Default value value at [0] must be a number (was a string)"
)
})
it("default object", () => {
const t = type({
Expand All @@ -425,13 +477,15 @@ contextualize(() => {
attest(() => {
// @ts-expect-error
type({ foo: type({ foo: "string" }).default({}) })
}).throws()
}).throws(
"ParseError: " + writeNonPrimitiveNonFunctionDefaultValueMessage("")
)
attest(() => {
type({
// @ts-expect-error
bar: type({ foo: "number" }).default(() => ({ foo: "foostr" }))
})
}).throws()
}).throws("ParseError: Default value foo must be a number (was a string)")
})
it("default factory", () => {
let i = 0
Expand Down
2 changes: 1 addition & 1 deletion ark/type/keywords/inference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ export type Optional = {
export type InferredOptional<t = unknown> = constrain<t, Optional>

export type Default<v = any> = {
default?: { value: v }
default?: { value: v extends Primitive ? v | (() => v) : () => v }
}

export type InferredDefault<t = unknown, v = any> = constrain<t, Default<v>>
Expand Down
6 changes: 4 additions & 2 deletions ark/type/parser/tuple.ts
Original file line number Diff line number Diff line change
Expand Up @@ -414,8 +414,10 @@ export type validateInfixExpression<def extends InfixExpression, $, args> =
: def[1] extends ":" ? Predicate<type.infer.Out<def[0], $, args>>
: def[1] extends "=>" ? Morph<type.infer.Out<def[0], $, args>>
: def[1] extends "@" ? MetaSchema
: def[1] extends "=" ? type.infer.Out<def[0], $, args>
: validateDefinition<def[2], $, args>
: def[1] extends "=" ?
| type.infer.Out<def[0], $, args>
| (() => type.infer.Out<def[0], $, args>)
: validateDefinition<def[2], $, args>
]

export type UnparsedTupleExpressionInput = {
Expand Down
8 changes: 6 additions & 2 deletions ark/type/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,12 @@ export interface TypeParser<$ = {}> extends Ark.boundTypeAttachments<$> {
one extends ":" ? [Predicate<distill.In<type.infer<zero, $>>>]
: one extends "=>" ? [Morph<distill.Out<type.infer<zero, $>>, unknown>]
: one extends "@" ? [MetaSchema]
: one extends "=" ? [distill.In<type.infer<NoInfer<zero>, $>>]
: [type.validate<rest[0], $>]
: one extends "=" ?
[
| distill.In<type.infer<NoInfer<zero>, $>>
| (() => distill.In<type.infer<NoInfer<zero>, $>>)
]
: [type.validate<rest[0], $>]
: []
): r

Expand Down

0 comments on commit 97de97a

Please sign in to comment.