Type API

NameSummaryNotes & Examples
$Scope

in which chained methods are parsed

infer

type of output this returns

🥸 inference-only property that will be undefined at runtime

const  = ("string").pipe(s => Number.parseInt(s))
type  = typeof .infer // number
inferIn

type of input this allows

🥸 inference-only property that will be undefined at runtime

const  = ("string").pipe(s => Number.parseInt(s))
type  = typeof .inferIn // string
json

internal JSON representation

toJsonSchema

generate a JSON Schema

meta

metadata like custom descriptions and error messages

✅ type

can be customized

for your project

description

human-readable English description

✅ works best for primitive values

const  = ("0 < number <= 100")
console.log(.description) // positive and at most 100
expression

syntax string similar to native TypeScript

✅ works well for both primitives and structures

const  = ({ coords: ["number", "number"] })
console.log(.expression) // { coords: [number, number] }
assert

validate and return transformed data or throw

✅ sugar to avoid checking for

type.errors

if they are unrecoverable

const  = ({
    superImportantValue: "string"
})
// throws AggregateError: superImportantValue must be a string (was missing)
const  = .assert({ irrelevantValue: "whoops" })
console.log(.superImportantValue) // valid output can be accessed directly
allows

check input without applying morphs

✅ good for stuff like filtering that doesn't benefit from detailed errors

const  = ("number | bigint")
// [0, 2n]
const  = [0, "one", 2n].filter(.allows)
configure

add metadata to shallow references

⚠️ does not affect error messages within properties of an object

const  = ("number % 2").configure({ description: "not odd" })
// all constraints at the root are affected
const  = notOdd(3) // must be not odd (was 3)
const  = notOdd("two") // must be not odd (was "two")

const  = ({
   // we should have referenced notOdd or added meta here
   notOdd: "number % 2",
// but instead chained from the root object
}).configure({ description: "not odd" })
// error message at path notOdd is not affected
const  = notOddBox({ notOdd: 3 }) // notOdd must be even (was 3)
// error message at root is affected, leading to a misleading description
const  = notOddBox(null) // must be not odd (was null)
describe

add description to shallow references

🔗 equivalent to .configure({ description }) (see

configure

)

⚠️ does not affect error messages within properties of an object

const  = (/^a.*z$/).describe("a string like 'a...z'")
const  = aToZ("alcatraz") // "alcatraz"
// ArkErrors: must be a string like 'a...z' (was "albatross")
const  = aToZ("albatross")
onUndeclaredKey

apply undeclared key behavior

"ignore" (default) - allow and preserve extra properties

"reject" - disallow extra properties

"delete" - clone and remove extra properties from output

onDeepUndeclaredKey

deeply apply undeclared key behavior

"ignore" (default) - allow and preserve extra properties

"reject" - disallow extra properties

"delete" - clone and remove extra properties from output

from

alias for

assert

with typed input

const  = ({ foo: "string" });
// TypeScript: foo must be a string (was 5)
const  = .from({ foo: 5 });
as

cast the way this is inferred

🥸 inference-only function that does nothing runtime

// Type<`LEEEEEEEE${string}ROY`>
const  = (/^LE{8,}ROY$/).as<`LEEEEEEEE${string}ROY`>()
brand

add a compile-time brand to output

🥸 inference-only function that does nothing runtime

const  = ("string")
    .narrow(s => s === [...s].reverse().join(""))
    .brand("palindrome")
// Brand<string, "palindrome">
const  = .assert("racecar")
and

intersect the parsed Type, throwing if the result is unsatisfiable

// Type<{ foo: number; bar: string }>
const  = ({ foo: "number" }).and({ bar: "string" })
// ParseError: Intersection at foo of number and string results in an unsatisfiable type
const  = ({ foo: "number" }).and({ foo: "string" })
or

union with the parsed Type

⚠️ a union that could apply different morphs to the same data is a ParseError ([docs](https://arktype.io/docs/expressions/union-morphs))

// Type<string | { box: string }>
const  = ("string").or({ box: "string" })
array

an array of this

// Type<{ rebmun: number }[]>
const  = ({ rebmun: "number" }).array();
optionaloptional definition

⚠️ unlike most other methods, this creates a definition rather than a Type (read why)

const  = ({ foo: "number" })
// Type<{ bar?: { foo: number } }>
const  = ({ bar: .optional() })
defaultdefaultable definition

✅ object defaults can be returned from a function

⚠️ throws if the default value is not allowed

⚠️ unlike most other methods, this creates a definition rather than a Type (read why)

// Type<{ count: Default<number, 0> }>
const  = ({ count: type.number.default(0) })
const  = ({ nested: "boolean" })
const  = ({
    key: nested.default(() => ({ nested: false }))
})
filter

apply a predicate function to input

⚠️ the behavior of

narrow

, this method's output counterpart, is usually more desirable

✅ most useful for morphs with input types that are re-used externally

🥸

Type predicates

can be used as casts

const  = ({ name: "string" }).pipe(user => JSON.stringify(user))
const  = .filter(user => user.name !== "Bobby Tables")
// Type<(In: `${string}Z`) => To<Date>>
const  = ("string.date.parse").filter((s): s is `${string}Z` =>
    s.endsWith("Z")
)
narrow

apply a predicate function to output

✅ go-to fallback for validation not composable via built-in types and operators

✅ runs after all other validators and morphs, if present

🥸

Type predicates

can be used as casts

const  = ("string").narrow(s => s === [...s].reverse().join(""))

const  = ("string.date.parse").narrow((date, ctx) =>
		date.getFullYear() === 2025 || ctx.mustBe("the current year")
)
// Type<`${string}.tsx`>
const  = ("string").narrow((s): s is `${string}.tsx` => /\.tsx?$/.test(s))
intersect

intersect the parsed Type, returning an introspectable

Disjoint

if the result is unsatisfiable

// Type<{ foo: number; bar: string }>
const  = ({ foo: "number" }).intersect({ bar: "string" })
const  = ("number > 10").intersect("number < 5")
// logs "Intersection of > 10 and < 5 results in an unsatisfiable type"
if ( instanceof Disjoint) console.log(`${.summary}`)
equals

check if the parsed Type's constraints are identical

✅ equal types have identical input and output constraints and transforms

✅ ignores associated

meta

, which does not affect the set of allowed values

const  = type.number.divisibleBy(6).moreThan(0)
// false (left side must also be positive)
.equals("number % 6")
// false (right side has an additional <100 constraint)
console.log(.equals("0 < (number % 6) < 100"))
const  = ("(number % 2) > 0").divisibleBy(3)
// true (types are normalized and reduced)
console.log(.equals())
ifEquals

narrow this based on an

equals

check

✅ ignores associated

meta

, which does not affect the set of allowed values

const  = type.raw(`${Math.random()}`)
// Type<0.5> | undefined
const  = .ifEquals("0.5")
extends

check if this is a subtype of the parsed Type

✅ a subtype must include all constraints from the base type

✅ unlike

equals

, additional constraints may be present

✅ ignores associated

meta

, which does not affect the set of allowed values

type.string.extends("unknown") // true
type.string.extends(/^a.*z$/) // false
ifExtends

narrow this based on an

extends

check

✅ ignores associated

meta

, which does not affect the set of allowed values

const  = (Math.random() > 0.5 ? "true" : "0") // Type<0 | true>
const  = .ifExtends("boolean") // Type<true> | undefined
overlaps

check if a value could satisfy this and the parsed Type

⚠️ will return true unless a

Disjoint

can be proven

type.string.overlaps("string | number") // true (e.g. "foo")
("string | number").overlaps("1") // true (1)
("number > 0").overlaps("number < 0") // false (no values exist)

const  = ("string").narrow(s => !s.includes("@"))
.overlaps("string.email") // true (no values exist, but not provable)
extract

extract branches

extend

ing the parsed Type

// Type<true | 0 | 2>
const  = ("boolean | 0 | 'one' | 2 | bigint").extract("number | 0n | true")
exclude

exclude branches

extend

ing the parsed Type

// Type<false | 'one' | bigint>
const  = ("boolean | 0 | 'one' | 2 | bigint").exclude("number | 0n | true")