# Objects

## properties

Objects definitions can include any combination of required, optional, defaultable named properties and index signatures.

### required

**string**

```ts
const symbolicKey = Symbol()

const MyObj = type({
	requiredKey: "string",
	// Nested definitions don't require additional `type` calls!
	[symbolicKey]: {
		nested: "unknown"
	}
})
```

**fluent**

```ts
const symbolicKey = Symbol()

const MyObj = type({
	requiredKey: type.string,
	// Nested definitions don't require additional `type` calls!
	[symbolicKey]: {
		nested: type.unknown
	}
})
```

### optional

**string**

```ts
const symbolicKey = Symbol()

const MyObj = type({
	"optionalKey?": "number[]",
	[symbolicKey]: "string?"
})
```

**fluent**

```ts
const symbolicKey = Symbol()

const MyObj = type({
	optionalKey: type.number.array().optional(),
	[symbolicKey]: type.string.optional()
})
```

**tuple**

```ts
const symbolicKey = Symbol()

const MyObj = type({
	optionalKey: [{ type: "'script'" }, "?"],
	[symbolicKey]: [{ ark: "'type'" }, "?"]
})
```

> **Should I use key or value syntax for optionality?**
>
> Optionality can either be expressed on the key or the corresponding value.
>
> We recommend using the key syntax by default because...
>
> - it mirrors TypeScript
> - it better reflects a _key presence_ constraint that has no effect on allowed values
>
> However, there are a few reasons you might want to use a value-embedded syntax:
>
> 1. The key is a symbol (makes key-embedded syntax impossible)
> 2. You want editor features like JSDoc and go-to-definition for the key (can't work if the key name changes)
> 3. You really hate having to quote key names

> **Optional properties cannot be present with the value undefined**
>
> In TypeScript, there is a setting called [`exactOptionalPropertyTypes`](https://www.typescriptlang.org/tsconfig#exactOptionalPropertyTypes) that can be set to `true` to enforce the distinction between properties that are missing and properties that are present with the value `undefined`.
>
> ArkType mirrors this behavior by default, so if you want to allow `undefined`, you'll need to add it to your value's definition. Though not recommended as a long-term solution, you may also [globally configure `exactOptionalPropertyTypes`](https://arktype.io/docs/configuration#exactoptionalpropertytypes) to `false`.
>
> <details>
> 	<summary>See an example</summary>
>
> ```ts
> const MyObj = type({
> 	"key?": "number"
> })
>
> // valid data
> const validResult = MyObj({})
>
> // Error: key must be a number (was undefined)
> const errorResult = MyObj({ key: undefined })
> ```
>
> </details>

### defaultable

**string**

```ts
const MyObj = type({
	defaultableKey: "boolean = false"
})
```

**fluent**

```ts
const MyObj = type({
	defaultableKey: type.boolean.default(false)
})
```

**tuple**

```ts
const MyObj = type({
	defaultableKey: ["boolean", "=", false]
})
```

An empty array or object can be defaulted directly in string syntax. A shared reference would leak mutations from one traversal into the next, so `= []` and `= {}` assign a new array or object each time the default is applied- exactly like the thunk form they replace.

```ts
const MyObj = type({
	// each parsed value gets its own array
	values: "string[] = []",
	meta: "object = {}"
})
```

> **Optional and default only work within objects and tuples!**
>
> Unlike e.g. `number.array()`, `number.optional()` and `number.default(0)` don't return a new `Type`, but rather a tuple definition like `[Type<number>, "?"]` or `[Type<number>, "=", 0]`.
>
> This reflects the fact that in ArkType's type system, optionality and defaultability are only meaningful in reference to a property. Attempting to create an optional or defaultable value outside an object like `type("string?")` will result in a `ParseError`.
>
> To create a `Type` accepting `string` or `undefined`, use a union like `type("string | undefined")`.
>
> To have it transform `undefined` to an empty string, use an explicit morph like:
>
> ```ts
> const FallbackString = type("string | undefined").pipe(v => v ?? "")
> ```

### index

**string**

```ts
const MyObj = type({
	// index signatures do not require a label
	"[string]": "number.integer",
	// arbitrary string or symbolic expressions are allowed
	"[string | symbol]": "number"
})
```

### undeclared

TypeScript's structural type system explicitly allows assigning objects with additional keys so long as all declared constraints are satisfied. ArkType mirrors this behavior by default because generally...

- Existing objects can be reused more often.
- Validation is much more efficient if you don't need to check for undeclared keys.
- Extra properties don't usually matter as long as those you've declared are satisfied.

However, sometimes the way you're using the object would make undeclared properties problematic. Even though they can't be reflected by TypeScript ([_yet_- please +1 the issue!](https://github.com/microsoft/TypeScript/issues/12936#issuecomment-1854411301)), ArkType _does_ support rejection or deletion of undeclared keys. This behavior can be defined for individual objects using the syntax below or [via configuration](https://arktype.io/docs/configuration#onundeclaredkey) if you want to change the default across all objects.

**string**

```ts
// fail if any key other than "onlyAllowedKey" is present
const MyClosedObject = type({
	"+": "reject",
	onlyAllowedKey: "string"
})

// delete all non-symbolic keys other than "onlyPreservedStringKey"
const MyStrippedObject = type({
	"+": "delete",
	"[symbol]": "unknown",
	onlyPreservedStringKey: "string"
})

// allow and preserve undeclared keys (the default behavior)
const MyOpenObject = type({
	// only specify "ignore" if you explicitly configured the default elsewhere
	"+": "ignore",
	nonexclusiveKey: "number"
})
```

**fluent**

```ts
// fail if any key other than "onlyAllowedKey" is present
const MyClosedObject = type({
	onlyAllowedKey: "string"
}).onUndeclaredKey("reject")

// delete all non-symbolic keys other than "onlyPreservedStringKey"
const MyStrippedObject = type({
	"[symbol]": "unknown",
	onlyPreservedStringKey: "string"
}).onUndeclaredKey("delete")

// allow and preserve undeclared keys (the default behavior)
const MyOpenObject = type({
	nonexclusiveKey: "number"
	// only specify "ignore" if you explicitly configured the default elsewhere
}).onUndeclaredKey("ignore")

// there is also a method for altering nested objects recursively
const MyDeeplyStrippedObject = type({
	preserved: "string",
	nested: {
		preserved: "string"
	}
}).onDeepUndeclaredKey("delete")
```

> **Prefer in-object syntax where possible**
>
> Certain methods like `.onUndeclaredKey` or `.configure` require a full traversal and transformation of the node created by the initial `type` call.
>
> <details>
> 	<summary>**Learn to recognize when chaining creates unnecessary overhead**</summary>
>
> Though they can be convenient if you need both variants of the type, most of the time you incur a significant performance cost instantiating your Type compared to the embedded syntax.
>
> Though how a Type is defined will never affect validation performance, depending on your sensitivity to initialization, you may want to avoid chained methods that transform rather than compose their base type. Methods like `.or` and `.pipe` that create new `Types` that directly reference the original incur no such overhead, so feel free to use whichever syntax is more convenient for those operations.
>
> </details>

### merge

**merge** allows you to extend an object with new properties or replace existing ones like the `...` operator [in JS](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax).

When applied to two distinct (i.e. non-overlapping) sets of properties, it is a more efficient equivalent of [intersection](https://arktype.io/docs/expressions#intersection).

However, if a key appears in both the base and merged objects, the base value will be discarded in favor of the merged rather than recursively intersected.

Spreading bypasses a lot of the behavioral complexity and computational overhead of an intersection and should be the preferred method of combining property sets.

Like ArkType's other [structural transformations](https://arktype.io/docs/objects#properties-structural), `merge` returns a new object built from the properties it extracts, so a root narrow or metadata is not carried over.

**fluent**

```ts
const zildjian = Symbol()

const Base = type({
	"[string]": "number",
	foo: "0",
	[zildjian]: "true"
})

// hover to see the inferred result
const chainedResult = Base.merge({
	"[string]": "bigint",
	"foo?": "1n"
})
```

**string**

```ts
const User = type({ isAdmin: "false", name: "string" })

// hover to see the newly merged object
const Admin = type({
	"...": User,
	// in an intersection, non-overlapping values at isAdmin would result in a ParseError
	isAdmin: "true",
	permissions: "string[]"
})
```

**generic**

```ts
const types = type.module({
	base: {
		"foo?": "0",
		"bar?": "0"
	},
	merged: {
		bar: "1",
		"baz?": "1"
	},
	result: "Merge<base, merged>"
})

// hover to see the inferred result
type Result = typeof types.result.infer
```

#### n-ary

To merge many objects directly, `type.merge` will avoid the need to chain or compose many binary expressions:

```ts
const zildjian = Symbol()

const Base = type({
	"[string]": "number",
	foo: "0",
	[zildjian]: "true"
})

// accepts ...objectDefinitions
const functionResult = type.merge(
	Base,
	{
		"[string]": "bigint",
		"foo?": "1n"
	},
	{
		includeThisPropAlso: "true"
	}
)
```

### keyof

Like in TypeScript, the `keyof` operator extracts the keys of an object as a union:

**fluent**

```ts
const UsedCar = type({
	originallyPurchased: "string.date",
	remainingWheels: "number"
})

const UsedCarKey = UsedCar.keyof()

type UsedCarKey = typeof UsedCarKey.infer
```

**string**

```ts
const types = type.module({
	UsedCar: {
		originallyPurchased: "string.date",
		remainingWheels: "number"
	},
	UsedCarKey: "keyof UsedCar"
})

type UsedCarKey = typeof types.UsedCarKey.infer
```

**tuple**

```ts
const UsedCar = type({
	originallyPurchased: "string.date",
	remainingWheels: "number"
})

const UsedCarKey = type(["keyof", UsedCar])

type UsedCarKey = typeof UsedCarKey.infer
```

**args**

```ts
const UsedCar = type({
	originallyPurchased: "string.date",
	remainingWheels: "number"
})

const UsedCarKey = type("keyof", UsedCar)

type UsedCarKey = typeof UsedCarKey.infer
```

Also like in TypeScript, if an object includes an index signature like `[string]` alongside named properties, the union from `keyof` will reduce to `string`:

```ts
const RecordWithSpecialKeys = type({
	"[string]": "unknown",
	verySpecialKey: "0 < number <= 3.14159",
	moderatelySpecialKey: "-9.51413 <= number < 0"
})

// in a union with the `string` index signature, string literals
// "verySpecialKey" and "moderatelySpecialKey" are redundant and will be pruned
const Key = RecordWithSpecialKeys.keyof()

// key is identical to the base `string` Type
console.log(Key.equals("string"))
```

> **ArkType's `keyof` will never include `number`**
>
> Though TypeScript's `keyof` operator can yield a `number`, the concept of
> numeric keys does not exist in JavaScript at runtime. This leads to confusing
> and inconsistent behavior. In ArkType, `keyof` will always return a `string`
> or `symbol` in accordance with the construction of a JavaScript object.
>
> <details>
> 	<summary>Learn more about our motivation for diverging from TypeScript on this issue</summary>
>
> In JavaScript, you can use a number literal to define a key, but the constructed value has no way to represent a numeric key, so it is coerced to a string.
>
> ```ts
> const numberLiteralObj = {
> 4: true,
> 5: true
> }
>
> const stringLiteralObj = {
> "4": true,
> "5": true
> }
>
> // numberLiteralObj and stringLiteralObj are indistinguishable at this point
> Object.keys(numberLiteralObj) // ["4", "5"]
> Object.keys(stringLiteralObj) // ["4", "5"]
> ```
>
> For a set-based type system to be correct, any two types representing the same set of underlying values must share a single representation. TypeScript's decision to have distinct numeric and string representations for the same underlying key has led to some if its most confusing inference pitfalls:
>
> ```ts
> type Thing1 = {
> [x: string]: unknown
> }
>
> // Thing2 is apparently identical to Thing1
> type Thing2 = Record<string, unknown>
>
> // and yet...
> type Key1 = keyof Thing1
>
> type Key2 = keyof Thing2
> ```
>
> This sort of inconsistency is inevitable for a type system that has to reconcile multiple representations
> for identical sets of underlying values. Therefore, numeric keys are one of a handful of cases where ArkType intentionally diverges from TypeScript. ArkType will never return a `number` from `keyof`. Keys will always be normalized to a `string` or `symbol`, the two distinct property types that can be uniquely attached to a JavaScript object.
>
> </details>

### get

Like an index access expression in TypeScript (e.g. `User["name"]`), the `get` operator extracts the Type of a value based on a specified key definition from an object:

```ts
const snorfUsage = type.enumerated("eating plants", "looking adorable")

const Manatee = type({
	isFriendly: "true",
	snorf: {
		uses: snorfUsage.array()
	}
})

const True = Manatee.get("isFriendly")

// nested properties can be accessed directly by passing additional args
const SnorfUses = Manatee.get("snorf", "uses")
```

> **Expressions like `get` and `omit` that extract a portion of an existing Type can be an antipattern!**
>
> Before using `get` to extract the type of a property you've defined, consider
> whether you may be able to define the property value directly as a standalone
> Type that can be easily referenced and composed as needed.
>
> Usually, composing Types from the bottom up is clearer and more efficient than trying to rip the part you need out of an existing Type.

Though cases like this are quite straightforward, there are a number of more nuanced behaviors to consider when accessing an arbitrary key that could be a union, literal, or index signature on an object Type that could also be a union including optional keys or index signatures.

If you're interested in a deeper dive into this (or anything else in ArkType), [our unit tests](https://github.com/arktypeio/arktype/blob/main/ark/type/__tests__/get.test.ts) are the closest thing we have to a comprehensive spec.

Not your cup of tea? No worries- the inferred types and errors you'll see in editor will always be guiding you in the right direction 🧭

> **Support for TypeScript's index access syntax is planned!**
>
> Leave a comment on [the
> issue](https://github.com/arktypeio/arktype/issues/831) letting us know if
> you're interested in using- or even helping implement- type-level parsing for
> string-embedded index access 🤓

### structural transformations

`merge`, `pick`, `omit`, `required`, `partial` and `map` each return a **new object built from the properties** of the `Type` they're applied to rather than referencing it.

> **Only properties are carried over**
>
> Anything attached to the object itself is not part of that result- most importantly a root [narrow](https://arktype.io/docs/expressions#narrow) or [filter](https://arktype.io/docs/expressions#filter), but also [metadata](https://arktype.io/docs/configuration#metadata) like `.describe(...)` and constraints like `.atLeastLength(...)`. Constraints on individual properties are unaffected.
>
> ```ts
> const User = type({
> 	name: "string",
> 	email: "string"
> }).narrow(user => user.email.includes("@"))
>
> // Type<{ name?: string; email?: string }>- the narrow is not a property
> const PartialUser = User.partial()
>
> // no error, though User itself would have rejected this
> const out = PartialUser({ email: "not an email" })
> ```
>
> Reapply anything you still need to the result:
>
> ```ts
> const PartialUser = type({
> 	name: "string",
> 	email: "string"
> })
> 	.partial()
> 	.narrow(user => user.email?.includes("@") ?? true)
> ```
>
> A root [morph](https://arktype.io/docs/expressions#pipe) is the exception. Since it can't be reduced to a set of properties, transforming a `Type` like `type({ name: "string" }).pipe(user => user)` throws a `ParseError` rather than discarding it.

### pick / omit

Extract or exclude specific properties from an object Type:

```ts
const User = type({
	name: "string",
	email: "string.email",
	"age?": "number"
})

// Type<{ name: string; email: string }>
const NameAndEmail = User.pick("name", "email")

// Type<{ name: string; age?: number }>
const WithoutEmail = User.omit("email")
```

These are also available as [generic keywords](https://arktype.io/docs/generics): `Pick(User, "name | email")`, `Omit(User, "email")`.

Like ArkType's other [structural transformations](https://arktype.io/docs/objects#properties-structural), `pick` and `omit` return a new object built from the properties they extract, so a root narrow or metadata is not carried over.

### required / partial

Make all named properties required or optional:

```ts
const User = type({
	name: "string",
	"email?": "string.email",
	"age?": "number"
})

// Type<{ name: string; email: string; age: number }>
const RequiredUser = User.required()

const Config = type({
	host: "string",
	port: "number"
})

// Type<{ host?: string; port?: number }>
const PartialConfig = Config.partial()
```

These are also available as [generic keywords](https://arktype.io/docs/generics): `Required(User)`, `Partial(Config)`.

Like ArkType's other [structural transformations](https://arktype.io/docs/objects#properties-structural), `required` and `partial` return a new object built from the properties they extract, so a root narrow or metadata is not carried over.

> **Arrays and tuples mirror TS's `Required` and `Partial`**
>
> Like the homomorphic mapped types they're named for, `required` and `partial` preserve an array or tuple base and apply to its elements:
>
> ```ts
> // Type<[string?, number?]>
> const PartialPair = type(["string", "number"]).partial()
>
> // Type<[string, number]>
> const RequiredPair = type(["string", "number?"]).required()
> ```
>
> They only ever change whether an element must be **present**, never the values it allows. TS additionally unions variadic elements and index signature values with `undefined`, since it has no modifier to mark either optional. ArkType doesn't, so `type("string[]").partial()` is still `string[]`.
>
> A postfix element can't be optional- `[string?, ...number[], boolean]` is as unrepresentable in ArkType as it is in TS. `partial` therefore throws a `ParseError` on a tuple like `[string, ...number[], boolean]` rather than widening every element the way TS's `Partial` does.

### readonly

Mark all properties as readonly (type-level only, no runtime effect):

```ts
const Frozen = type({
	id: "number",
	name: "string"
}).readonly()

// Type<{ readonly id: number; readonly name: string }>
```

### map

Transform the properties of an object Type using a mapping function. The mapper receives a prop entry with `key`, `value`, and `kind` (`"required"` or `"optional"`). Return a `{ key, value }` object (optionally with `kind`) to transform, or an empty array `[]` to remove the property.

Like ArkType's other [structural transformations](https://arktype.io/docs/objects#properties-structural), `map` returns a new object built from the properties it extracts, so a root narrow or metadata is not carried over.

```ts
const User = type({
	name: "string",
	"age?": "number"
})

// make all properties nullable by wrapping each value in a union with null
const NullableUser = User.map(prop => {
	return { key: prop.key, value: prop.value.or("null") }
})
// Type<{ name: string | null; age?: number | null }>
```

### props

The `props` getter returns an array of property descriptors for introspection:

```ts
const User = type({
	name: "string",
	"age?": "number",
	role: "'admin' | 'user' = 'user'"
})

for (const prop of User.props) {
	console.log(prop.kind) // "required", "optional"
	console.log(prop.key) // "name", "role", "age"
	console.log(prop.value.expression) // "string", '"admin" | "user"', "number"
	console.log(prop.meta) // metadata object
	if ("default" in prop) console.log(prop.default) // "user" for role
}
```

## arrays

**string**

```ts
const Arrays = type({
	key: "string[]"
})
```

**fluent**

```ts
const Arrays = type({
	key: type.string.array()
})
```

**tuple**

```ts
const Arrays = type({
	key: [{ name: "string" }, "[]"]
})
```

**args**

```ts
const Arrays = type({
	key: type({ name: "string" }, "[]")
})
```

### lengths

Constrain an array with an inclusive or exclusive min or max length.

**string**

```ts
const Bounded = type({
	nonEmptyStringArray: "string[] > 0",
	atLeast3Integers: "number.integer[] >= 3",
	lessThan10Emails: "string.email[] < 10",
	atMost5Booleans: "boolean[] <= 5"
})
```

**fluent**

```ts
const Bounded = type({
	nonEmptyStringArray: type.string.array().moreThanLength(0),
	atLeast3Integers: type.keywords.number.integer.array().atLeastLength(3),
	lessThan10Emails: type.keywords.string.email.array().lessThanLength(10),
	atMost5Booleans: type.boolean.array().atMostLength(5)
})
```

Range expressions allow you to specify both a min and max length and use the same syntax for exclusivity.

**string**

```ts
const Range = type({
	nonEmptyStringArrayAtMostLength10: "0 < string[] <= 10",
	twoToFiveIntegers: "2 <= number.integer[] < 6"
})
```

**fluent**

```ts
const Range = type({
	nonEmptyStringArrayAtMostLength10: type.string
		.array()
		.moreThanLength(0)
		.atMostLength(10),
	twoToFiveIntegers: type.keywords.number.integer
		.array()
		.atLeastLength(2)
		.lessThanLength(6)
})
```

## tuples

Like objects, tuples are structures whose values are nested definitions. Like TypeScript, ArkType supports prefix, optional, variadic, and postfix elements, with the same restrictions about combining them.

### prefix

**string**

```ts
const MyTuple = type([
	"string",
	// Object definitions can be nested in tuples- and vice versa!
	{
		coordinates: ["number", "number"]
	}
])
```

**fluent**

```ts
const MyTuple = type([
	type.string,
	// Object definitions can be nested in tuples- and vice versa!
	{
		coordinates: [type.number, type.number]
	}
])
```

### defaultable

Defaultable elements are optional elements that will be assigned their specified default if not present in the tuple's input.

A tuple may include zero or more defaultable elements following its prefix elements and preceding its non-defaultable optional elements.

Like optional elements, defaultable elements are mutually exclusive with postfix elements.

**string**

```ts
const MyTuple = type(["string", "boolean = false", "number = 0"])
```

**fluent**

```ts
const MyTuple = type([
	type.string,
	type.boolean.default(false),
	type.number.default(0)
])
```

**tuple**

```ts
const MyTuple = type([
	"string",
	[
		{
			name: "string"
		},
		"=",
		() => ({ name: "Anon Eemuss" })
	]
])
```

### optional

Optional elements are tuple elements that may or may not be present in the input that do not have a default value.

A tuple may include zero or more optional elements following its prefix and defaultable elements and preceding either a variadic element or the end of the tuple.

Like in TypeScript, optional elements are mutually exclusive with postfix elements.

**string**

```ts
const MyTuple = type(["string", "bigint = 999n", "boolean?", "number?"])
```

**fluent**

```ts
const MyTuple = type([
	type.string,
	type.bigint.default(999n),
	type.boolean.optional(),
	type.number.optional()
])
```

**tuple**

```ts
const MyTuple = type([
	"string",
	[
		{
			name: "string"
		},
		"?"
	]
])
```

### variadic

Like in TypeScript, variadic elements allow zero or more consecutive values of a given type and may occur at most once in a tuple.

They are specified with a `"..."` operator preceding an array element.

**string**

```ts
// allows a string followed by zero or more numbers
const MyTuple = type(["string", "...", "number[]"])
```

**fluent**

```ts
// allows a string followed by zero or more numbers
const MyTuple = type([type.string, "...", type.number.array()])
```

### postfix

Postfix elements are required elements following a variadic element.

They are mutually exclusive with optional elements.

**string**

```ts
// allows zero or more numbers followed by a boolean, then a string
const MyTuple = type(["...", "number[]", "boolean", "string"])
```

**fluent**

```ts
// allows zero or more numbers followed by a boolean, then a string
const MyTuple = type(["...", type.number.array(), type.boolean, type.string])
```

## dates

### literals

Date literals represent a Date instance with an exact value.

They're primarily useful in ranges.

```ts
const Literals = type({
	singleQuoted: "d'01-01-1970'",
	doubleQuoted: 'd"01-01-1970"'
})
```

### ranges

Constrain a Date with an inclusive or exclusive min or max.

Bounds can be expressed as either a [number](https://arktype.io/docs/primitives#number-literals) representing its corresponding Unix epoch value or a [Date literal](https://arktype.io/docs/objects#dates-literals).

**string**

```ts
const Bounded = type({
	dateInThePast: `Date < ${Date.now()}`,
	dateAfter2000: "Date > d'2000-01-01'",
	dateAtOrAfter1970: "Date >= 0"
})
```

**fluent**

```ts
const Bounded = type({
	dateInThePast: type.Date.earlierThan(Date.now()),
	dateAfter2000: type.Date.laterThan("2000-01-01"),
	dateAtOrAfter1970: type.Date.atOrAfter(0)
})
```

Range expressions allow you to specify both a min and max and use the same syntax for exclusivity.

**string**

```ts
const tenYearsAgo = new Date()
	.setFullYear(new Date().getFullYear() - 10)
	.valueOf()

const Bounded = type({
	dateInTheLast10Years: `${tenYearsAgo} <= Date < ${Date.now()}`
})
```

**fluent**

```ts
const tenYearsAgo = new Date()
	.setFullYear(new Date().getFullYear() - 10)
	.valueOf()

const Bounded = type({
	dateInTheLast10Years: type.Date.atOrAfter(tenYearsAgo).earlierThan(Date.now())
})
```

## instanceof

Most built-in instance types like `Array` and `Date` are available directly as keywords, but `instanceof` can be useful for constraining a type to one of your own classes.

> **Array is the only constructor that works cross-realm**
>
> Constructors are compared by identity, so a value created in another realm (e.g. an `<iframe>` or a `node:vm` context) will not be an `instanceof` the constructor in your realm.
>
> `Array` is special-cased to use `Array.isArray`, making it the only built-in constructor that will also match arrays from another realm.

**fluent**

```ts
class MyClass {}

const Instances = type.instanceOf(MyClass)
```

**tuple**

```ts
class MyClass {}

const Instances = type({
	key: ["instanceof", MyClass]
})
```

**args**

```ts
class MyClass {}

const Instances = type({
	key: type("instanceof", MyClass)
})
```

### keywords

A list of instanceof keywords can be found [here](https://arktype.io/docs/keywords#instanceof) alongside the base and subtype keywords for [Array](https://arktype.io/docs/keywords#array) and [FormData](https://arktype.io/docs/keywords#formdata).
