From 7c2711ef591cec9ea791684a5b82e095f38c460f Mon Sep 17 00:00:00 2001 From: WolfieLeader Date: Thu, 19 Mar 2026 04:48:19 +0200 Subject: [PATCH 01/10] docs: add syntax cheat sheet page Single-page quick reference covering syntax kinds, keywords mental model, operators, objects, arrays, composition, morphs, type inference, error handling, and common gotchas. Placed after Intro in sidebar navigation. Addresses #1562 per maintainer request for "markdown page with code and comments" covering common operators, keyword patterns, syntax kinds, and common issues new users run into. --- ark/docs/content/docs/cheat-sheet.mdx | 159 ++++++++++++++++++++++++++ ark/docs/content/docs/meta.json | 1 + 2 files changed, 160 insertions(+) create mode 100644 ark/docs/content/docs/cheat-sheet.mdx diff --git a/ark/docs/content/docs/cheat-sheet.mdx b/ark/docs/content/docs/cheat-sheet.mdx new file mode 100644 index 0000000000..f00eef1e07 --- /dev/null +++ b/ark/docs/content/docs/cheat-sheet.mdx @@ -0,0 +1,159 @@ +--- +title: Cheat Sheet +--- + +A quick reference for ArkType's most common patterns. For full docs, explore the sidebar. + +## Syntax Kinds + +ArkType offers three ways to define the same type. Use whichever fits your context. + +```ts +import { type } from "arktype" + +// string expression — most concise +const StringSyntax = type("string | number") + +// tuple expression — embed non-string operands +const TupleSyntax = type(["string", "|", "number"]) + +// chained — compose existing types +const Chained = type("string").or("number") +``` + +## Keywords + +Keywords follow a `typescriptType.constraint.subconstraint` pattern: + +```ts +// string keywords +type("string.email") // email format +type("string.uuid") // UUID format +type("string.url") // parsable URL +type("string.trim") // morph: trims whitespace +type("string.json.parse") // morph: string → parsed JSON +type("string.date.parse") // morph: string → Date + +// number keywords +type("number.integer") // whole numbers +type("number.finite") // no Infinity or NaN +type("number.safe") // within safe integer range +type("number.epoch") // valid Unix timestamp +``` + +## Operators + +```ts +type("string | number") // union +type("string & /@foo/") // intersection +type("number > 0") // exclusive min +type("number >= 0") // inclusive min +type("string <= 255") // max length +type("string.email = 'n/a'") // default value +``` + +## Objects + +```ts +const User = type({ + name: "string", + "email?": "string.email", // optional key — use '?' suffix in the key + "age?": "number >= 0 = 0" // optional with default +}) + +// index signatures (Record types) +const Env = type({ + "[string]": "string" // Record +}) + +// strip undeclared keys +const Strict = User.onUndeclaredKey("delete") +``` + +## Arrays and Tuples + +```ts +const Strings = type("string[]") // string expression +const Numbers = type.number.array() // chained + +// tuples — fixed length and positional types +const Pair = type(["string", "number"]) + +// GOTCHA: type([MyType]) is a 1-element tuple, NOT an array +const Wrong = type([MyType]) // tuple of exactly [MyType] +const Right = MyType.array() // MyType[] + +// readonly +const Ids = type("string[]").readonly() // readonly string[] +``` + +## Composition + +```ts +const Device = type({ + platform: "'android' | 'ios'", + "version?": "string" +}) + +// reference in another type — just pass it directly +const User = type({ + name: "string", + device: Device +}) + +// fluent composition +const Admin = User.and({ role: "'admin'" }) +const Guest = type({ name: "string" }).or({ token: "string" }) +``` + +## Morphs and Pipes + +```ts +// .to() — validated input → validated output +const ParsedInt = type("string").to("number.integer") + +// .pipe() — validated input → transform function +const ToUpper = type("string").pipe(s => s.toUpperCase()) + +// built-in parse morphs +const ParseDate = type("string.date.parse") // string → Date +const ParseJson = type("string.json.parse") // string → object +const Trimmed = type("string.trim") // string → trimmed string +``` + +## Type Inference + +```ts +const User = type({ + name: "string", + "age?": "number" +}) + +// extract the TypeScript type +type User = typeof User.infer +// { name: string; age?: number } +``` + +## Error Handling + +```ts +const out = User(unknownData) + +if (out instanceof type.errors) { + // ArkErrors — array-like with .summary + console.error(out.summary) +} else { + // out is typed as User + console.log(out.name) +} +``` + +## Common Gotchas + +| Mistake | Fix | +|---------|-----| +| `name: "string \| undefined"` for optional | Use `"name?": "string"` — append `?` to the **key** | +| `type([Schema])` for arrays | Use `Schema.array()` — brackets create tuples | +| `"Record"` for records | Use `type({ "[string]": T })` — index signature syntax | +| Manually re-validating after `type()` | Trust ArkType's output — it already validated the structure | +| `type User = typeof Schema.t` | Use `typeof Schema.infer` for the output type | diff --git a/ark/docs/content/docs/meta.json b/ark/docs/content/docs/meta.json index b2f7ab553e..bdcbcb575c 100644 --- a/ark/docs/content/docs/meta.json +++ b/ark/docs/content/docs/meta.json @@ -1,6 +1,7 @@ { "pages": [ "intro", + "cheat-sheet", "primitives", "objects", "keywords", From e64c1884385ec4803ec28edeea0cb4b85b439b6a Mon Sep 17 00:00:00 2001 From: WolfieLeader Date: Thu, 19 Mar 2026 04:49:12 +0200 Subject: [PATCH 02/10] docs: add Cursor rules file for AI/LLM usage Adds .cursor/rules/arktype.mdc with common ArkType patterns and pitfalls for AI coding assistants. Adapted from the battle-tested cheat sheet in #1539 that was shared across teams adopting ArkType with Cursor. Addresses #1539 follow-up item for improving LLM knowledge. --- .cursor/rules/arktype.mdc | 113 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 .cursor/rules/arktype.mdc diff --git a/.cursor/rules/arktype.mdc b/.cursor/rules/arktype.mdc new file mode 100644 index 0000000000..af13a632e1 --- /dev/null +++ b/.cursor/rules/arktype.mdc @@ -0,0 +1,113 @@ +--- +description: When working with ArkType for schema validation. Covers common patterns, pitfalls, and idiomatic usage. +globs: +alwaysApply: false +--- + +# ArkType Usage Guidelines + +## Optional Keys + +```ts +// ❌ WRONG +type({ name: "string | undefined" }) + +// ✅ CORRECT — append ? to the key +type({ "name?": "string" }) +``` + +## Schema References + +**In objects** — pass schemas directly: +```ts +const Inner = type({ foo: "string" }) +const Outer = type({ inner: Inner }) +``` + +**In records** — use index signature syntax: +```ts +// ❌ WRONG: "Record" +// ✅ CORRECT: +type({ "[string]": Schema }) +``` + +**In arrays** — use `.array()`: +```ts +// ❌ WRONG: type([Schema]) — creates a 1-element tuple +// ✅ CORRECT: +Schema.array() +``` + +## Type Inference + +```ts +type User = typeof UserSchema.infer +``` + +## Type Declaration + +Match a schema to an existing TypeScript type: +```ts +const Schema = type.declare().type({ + items: type.string.array().readonly() // readonly string[] +}) +``` + +## Error Handling + +```ts +const out = Schema(data) +if (out instanceof type.errors) { + console.error(out.summary) +} +``` + +## Syntax Kinds + +Three equivalent ways to define types: + +```ts +// string expression +type("string | number") + +// tuple expression +type(["string", "|", "number"]) + +// chained +type("string").or("number") +``` + +## Keywords Pattern + +Keywords follow `typescriptType.constraint.subconstraint`: + +- `string.email`, `string.uuid`, `string.url` +- `number.integer`, `number.finite`, `number.safe` +- `string.trim`, `string.json.parse`, `string.date.parse` (morphs) + +## Operators + +```ts +type("string | number") // union +type("string & /@pattern/") // intersection +type("number > 0") // exclusive min +type("number >= 0") // inclusive min +type("string <= 255") // max length +type("string = 'default'") // default value +``` + +## Morphs + +```ts +type("string").to("number.integer") // validated transform +type("string").pipe(s => s.trim()) // custom transform +type("string.json.parse") // built-in parse morph +``` + +## Common Mistakes + +1. `type([X])` creates a tuple, not an array — use `X.array()` +2. `"string | undefined"` for optional — use `"key?": "string"` +3. `"Record"` — use `type({ "[string]": T })` +4. Re-validating after `type()` passes — trust ArkType's output +5. `typeof Schema.t` — use `typeof Schema.infer` for output type From 839de74239f38044ea8439540c1dac55f3a35ed8 Mon Sep 17 00:00:00 2001 From: WolfieLeader Date: Thu, 19 Mar 2026 04:50:45 +0200 Subject: [PATCH 03/10] fix: remove number.finite reference (not yet on main) --- .cursor/rules/arktype.mdc | 2 +- ark/docs/content/docs/cheat-sheet.mdx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.cursor/rules/arktype.mdc b/.cursor/rules/arktype.mdc index af13a632e1..5586a1cf85 100644 --- a/.cursor/rules/arktype.mdc +++ b/.cursor/rules/arktype.mdc @@ -82,7 +82,7 @@ type("string").or("number") Keywords follow `typescriptType.constraint.subconstraint`: - `string.email`, `string.uuid`, `string.url` -- `number.integer`, `number.finite`, `number.safe` +- `number.integer`, `number.safe`, `number.epoch` - `string.trim`, `string.json.parse`, `string.date.parse` (morphs) ## Operators diff --git a/ark/docs/content/docs/cheat-sheet.mdx b/ark/docs/content/docs/cheat-sheet.mdx index f00eef1e07..cdb6fede5c 100644 --- a/ark/docs/content/docs/cheat-sheet.mdx +++ b/ark/docs/content/docs/cheat-sheet.mdx @@ -36,7 +36,6 @@ type("string.date.parse") // morph: string → Date // number keywords type("number.integer") // whole numbers -type("number.finite") // no Infinity or NaN type("number.safe") // within safe integer range type("number.epoch") // valid Unix timestamp ``` From 186f695682040dc5d19f88df3b45a14b74d42e9f Mon Sep 17 00:00:00 2001 From: WolfieLeader Date: Thu, 19 Mar 2026 04:51:58 +0200 Subject: [PATCH 04/10] docs: add CLAUDE.md and AGENTS.md for AI agent context Provides ArkType-specific guidance for Claude Code, Codex, and other AI agents that read CLAUDE.md/AGENTS.md. Covers monorepo structure, code style, key patterns, syntax cheat sheet, and common gotchas. AGENTS.md is symlinked to CLAUDE.md so both agent conventions are served from a single source of truth. --- AGENTS.md | 1 + CLAUDE.md | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 120000 AGENTS.md create mode 100644 CLAUDE.md diff --git a/AGENTS.md b/AGENTS.md new file mode 120000 index 0000000000..681311eb9c --- /dev/null +++ b/AGENTS.md @@ -0,0 +1 @@ +CLAUDE.md \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..12c5c2ae11 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,81 @@ +# ArkType — Agent Context + +ArkType is TypeScript's 1:1 validator, optimized from editor to runtime. It parses TypeScript-like string syntax at runtime — this is unique among validation libraries. + +## Quick Start + +```bash +pnpm i && pnpm build # install and build all packages +pnpm prChecks # lint + build + test (run before PRs) +pnpm testTyped --skipTypes # run tests without type checking +``` + +## Monorepo Structure + +- `ark/type/` — main validation library (`arktype` on npm) +- `ark/schema/` — schema layer (constraint nodes, refinements, scopes) +- `ark/util/` — shared utilities +- `ark/docs/` — documentation site (Fumadocs/Next.js) +- `ark/json-schema/` — JSON Schema conversion +- `ark/regex/` — ArkRegex type-safe regex +- `ark/attest/` — custom assertion/test library +- `ark/fast-check/` — property testing integration + +## Key Patterns + +**String keywords** follow `typescriptType.constraint.subconstraint`: +- `string.email`, `string.uuid`, `string.url`, `string.hex` +- `number.integer`, `number.safe`, `number.epoch` +- Morphs: `string.trim`, `string.json.parse`, `string.date.parse` + +**Adding a regex string validator:** +```ts +const myValidator = regexStringNode(/^pattern$/, "description") +``` +Register in `Scope.module()` in `ark/type/keywords/string.ts` and add to the namespace `$` type. + +**Adding a number keyword:** +Use `rootSchema()` with `domain`, `min`/`max`, or `predicate` constraints. See `number.safe` in `ark/type/keywords/number.ts`. + +## Code Style + +- Tabs, no semicolons, no trailing commas (`prettier` enforced) +- `experimentalTernaries: true` — `?` at end of line, `:` on next line +- `arrowParens: "avoid"` — `x => x` not `(x) => x` +- Tests use `attest` (custom lib) with `.snap()` for snapshots + +## ArkType Syntax Cheat Sheet + +```ts +// optional keys — append ? to the key name +type({ "name?": "string" }) + +// arrays — use .array(), NOT type([Schema]) +Schema.array() + +// records — use index signature syntax +type({ "[string]": ValueSchema }) + +// type inference +type User = typeof UserSchema.infer + +// error handling +const out = Schema(data) +if (out instanceof type.errors) { + console.error(out.summary) +} + +// three syntax kinds (equivalent): +type("string | number") // string expression +type(["string", "|", "number"]) // tuple expression +type("string").or("number") // chained +``` + +## Common Gotchas + +- `type([X])` creates a 1-element **tuple**, not an array — use `X.array()` +- `"string | undefined"` for optional — use `"key?": "string"` instead +- `"Record"` doesn't work — use `type({ "[string]": T })` +- `safeParse()` doesn't exist — call the type directly: `const out = Schema(data)` +- `auth-schema.ts` is generated — never edit it +- `regexStringNode()` patterns must be anchored with `^`/`$` From 146db7caa055b44c605b4cbc7ab49ab8c4adffe1 Mon Sep 17 00:00:00 2001 From: WolfieLeader Date: Thu, 19 Mar 2026 04:52:55 +0200 Subject: [PATCH 05/10] fix: move default value example inside object context --- ark/docs/content/docs/cheat-sheet.mdx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ark/docs/content/docs/cheat-sheet.mdx b/ark/docs/content/docs/cheat-sheet.mdx index cdb6fede5c..ad4c0fe624 100644 --- a/ark/docs/content/docs/cheat-sheet.mdx +++ b/ark/docs/content/docs/cheat-sheet.mdx @@ -43,12 +43,14 @@ type("number.epoch") // valid Unix timestamp ## Operators ```ts -type("string | number") // union -type("string & /@foo/") // intersection -type("number > 0") // exclusive min -type("number >= 0") // inclusive min -type("string <= 255") // max length -type("string.email = 'n/a'") // default value +type("string | number") // union +type("string & /@foo/") // intersection +type("number > 0") // exclusive min +type("number >= 0") // inclusive min +type("string <= 255") // max length + +// defaults — only valid inside objects/tuples +type({ "email?": "string.email = 'n/a'" }) ``` ## Objects From 93521ba238c985b266a11c95a73361eba25b2991 Mon Sep 17 00:00:00 2001 From: WolfieLeader Date: Thu, 19 Mar 2026 04:53:50 +0200 Subject: [PATCH 06/10] fix: defaults make keys implicitly optional, remove redundant ? --- ark/docs/content/docs/cheat-sheet.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ark/docs/content/docs/cheat-sheet.mdx b/ark/docs/content/docs/cheat-sheet.mdx index ad4c0fe624..c7ec635065 100644 --- a/ark/docs/content/docs/cheat-sheet.mdx +++ b/ark/docs/content/docs/cheat-sheet.mdx @@ -49,8 +49,8 @@ type("number > 0") // exclusive min type("number >= 0") // inclusive min type("string <= 255") // max length -// defaults — only valid inside objects/tuples -type({ "email?": "string.email = 'n/a'" }) +// defaults — valid inside objects (key becomes implicitly optional) +type({ email: "string.email = 'n/a'" }) ``` ## Objects @@ -58,8 +58,8 @@ type({ "email?": "string.email = 'n/a'" }) ```ts const User = type({ name: "string", - "email?": "string.email", // optional key — use '?' suffix in the key - "age?": "number >= 0 = 0" // optional with default + "email?": "string.email", // optional key — use '?' suffix in the key + age: "number >= 0 = 0" // default value — implicitly optional }) // index signatures (Record types) From f308dcd09f9ce12e1036460eb41e475d085efb52 Mon Sep 17 00:00:00 2001 From: WolfieLeader Date: Thu, 19 Mar 2026 04:54:21 +0200 Subject: [PATCH 07/10] fix: define Item type to resolve MyType reference in code block --- ark/docs/content/docs/cheat-sheet.mdx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ark/docs/content/docs/cheat-sheet.mdx b/ark/docs/content/docs/cheat-sheet.mdx index c7ec635065..37d6f326b7 100644 --- a/ark/docs/content/docs/cheat-sheet.mdx +++ b/ark/docs/content/docs/cheat-sheet.mdx @@ -80,9 +80,10 @@ const Numbers = type.number.array() // chained // tuples — fixed length and positional types const Pair = type(["string", "number"]) -// GOTCHA: type([MyType]) is a 1-element tuple, NOT an array -const Wrong = type([MyType]) // tuple of exactly [MyType] -const Right = MyType.array() // MyType[] +// GOTCHA: type([X]) is a 1-element tuple, NOT an array +const Item = type({ id: "string" }) +const Wrong = type([Item]) // tuple of exactly [Item] +const Right = Item.array() // Item[] // readonly const Ids = type("string[]").readonly() // readonly string[] From 3e788c86eaa57417c1f620d2a2228d6049da792f Mon Sep 17 00:00:00 2001 From: WolfieLeader Date: Thu, 19 Mar 2026 04:54:51 +0200 Subject: [PATCH 08/10] fix: make error handling code block self-contained --- ark/docs/content/docs/cheat-sheet.mdx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ark/docs/content/docs/cheat-sheet.mdx b/ark/docs/content/docs/cheat-sheet.mdx index 37d6f326b7..ead4398787 100644 --- a/ark/docs/content/docs/cheat-sheet.mdx +++ b/ark/docs/content/docs/cheat-sheet.mdx @@ -139,13 +139,18 @@ type User = typeof User.infer ## Error Handling ```ts -const out = User(unknownData) +const User = type({ + name: "string", + "age?": "number" +}) + +const out = User({ name: "Alan", age: "not a number" }) if (out instanceof type.errors) { // ArkErrors — array-like with .summary console.error(out.summary) } else { - // out is typed as User + // out is typed as { name: string; age?: number } console.log(out.name) } ``` From 8ce231446e17ca595221a799b855cf505842b23f Mon Sep 17 00:00:00 2001 From: WolfieLeader Date: Thu, 19 Mar 2026 04:57:08 +0200 Subject: [PATCH 09/10] style: fix prettier formatting in CLAUDE.md and cheat-sheet.mdx --- CLAUDE.md | 11 +++-- ark/docs/content/docs/cheat-sheet.mdx | 60 +++++++++++++-------------- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 12c5c2ae11..69a89e8467 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -24,14 +24,17 @@ pnpm testTyped --skipTypes # run tests without type checking ## Key Patterns **String keywords** follow `typescriptType.constraint.subconstraint`: + - `string.email`, `string.uuid`, `string.url`, `string.hex` - `number.integer`, `number.safe`, `number.epoch` - Morphs: `string.trim`, `string.json.parse`, `string.date.parse` **Adding a regex string validator:** + ```ts const myValidator = regexStringNode(/^pattern$/, "description") ``` + Register in `Scope.module()` in `ark/type/keywords/string.ts` and add to the namespace `$` type. **Adding a number keyword:** @@ -62,13 +65,13 @@ type User = typeof UserSchema.infer // error handling const out = Schema(data) if (out instanceof type.errors) { - console.error(out.summary) + console.error(out.summary) } // three syntax kinds (equivalent): -type("string | number") // string expression -type(["string", "|", "number"]) // tuple expression -type("string").or("number") // chained +type("string | number") // string expression +type(["string", "|", "number"]) // tuple expression +type("string").or("number") // chained ``` ## Common Gotchas diff --git a/ark/docs/content/docs/cheat-sheet.mdx b/ark/docs/content/docs/cheat-sheet.mdx index ead4398787..37f1495838 100644 --- a/ark/docs/content/docs/cheat-sheet.mdx +++ b/ark/docs/content/docs/cheat-sheet.mdx @@ -27,27 +27,27 @@ Keywords follow a `typescriptType.constraint.subconstraint` pattern: ```ts // string keywords -type("string.email") // email format -type("string.uuid") // UUID format -type("string.url") // parsable URL -type("string.trim") // morph: trims whitespace -type("string.json.parse") // morph: string → parsed JSON -type("string.date.parse") // morph: string → Date +type("string.email") // email format +type("string.uuid") // UUID format +type("string.url") // parsable URL +type("string.trim") // morph: trims whitespace +type("string.json.parse") // morph: string → parsed JSON +type("string.date.parse") // morph: string → Date // number keywords -type("number.integer") // whole numbers -type("number.safe") // within safe integer range -type("number.epoch") // valid Unix timestamp +type("number.integer") // whole numbers +type("number.safe") // within safe integer range +type("number.epoch") // valid Unix timestamp ``` ## Operators ```ts -type("string | number") // union -type("string & /@foo/") // intersection -type("number > 0") // exclusive min -type("number >= 0") // inclusive min -type("string <= 255") // max length +type("string | number") // union +type("string & /@foo/") // intersection +type("number > 0") // exclusive min +type("number >= 0") // inclusive min +type("string <= 255") // max length // defaults — valid inside objects (key becomes implicitly optional) type({ email: "string.email = 'n/a'" }) @@ -58,13 +58,13 @@ type({ email: "string.email = 'n/a'" }) ```ts const User = type({ name: "string", - "email?": "string.email", // optional key — use '?' suffix in the key - age: "number >= 0 = 0" // default value — implicitly optional + "email?": "string.email", // optional key — use '?' suffix in the key + age: "number >= 0 = 0" // default value — implicitly optional }) // index signatures (Record types) const Env = type({ - "[string]": "string" // Record + "[string]": "string" // Record }) // strip undeclared keys @@ -74,7 +74,7 @@ const Strict = User.onUndeclaredKey("delete") ## Arrays and Tuples ```ts -const Strings = type("string[]") // string expression +const Strings = type("string[]") // string expression const Numbers = type.number.array() // chained // tuples — fixed length and positional types @@ -82,8 +82,8 @@ const Pair = type(["string", "number"]) // GOTCHA: type([X]) is a 1-element tuple, NOT an array const Item = type({ id: "string" }) -const Wrong = type([Item]) // tuple of exactly [Item] -const Right = Item.array() // Item[] +const Wrong = type([Item]) // tuple of exactly [Item] +const Right = Item.array() // Item[] // readonly const Ids = type("string[]").readonly() // readonly string[] @@ -118,9 +118,9 @@ const ParsedInt = type("string").to("number.integer") const ToUpper = type("string").pipe(s => s.toUpperCase()) // built-in parse morphs -const ParseDate = type("string.date.parse") // string → Date -const ParseJson = type("string.json.parse") // string → object -const Trimmed = type("string.trim") // string → trimmed string +const ParseDate = type("string.date.parse") // string → Date +const ParseJson = type("string.json.parse") // string → object +const Trimmed = type("string.trim") // string → trimmed string ``` ## Type Inference @@ -157,10 +157,10 @@ if (out instanceof type.errors) { ## Common Gotchas -| Mistake | Fix | -|---------|-----| -| `name: "string \| undefined"` for optional | Use `"name?": "string"` — append `?` to the **key** | -| `type([Schema])` for arrays | Use `Schema.array()` — brackets create tuples | -| `"Record"` for records | Use `type({ "[string]": T })` — index signature syntax | -| Manually re-validating after `type()` | Trust ArkType's output — it already validated the structure | -| `type User = typeof Schema.t` | Use `typeof Schema.infer` for the output type | +| Mistake | Fix | +| ------------------------------------------ | ----------------------------------------------------------- | +| `name: "string \| undefined"` for optional | Use `"name?": "string"` — append `?` to the **key** | +| `type([Schema])` for arrays | Use `Schema.array()` — brackets create tuples | +| `"Record"` for records | Use `type({ "[string]": T })` — index signature syntax | +| Manually re-validating after `type()` | Trust ArkType's output — it already validated the structure | +| `type User = typeof Schema.t` | Use `typeof Schema.infer` for the output type | From 8805e916e3f8737ae9e24fd9b11b0b67af414ae1 Mon Sep 17 00:00:00 2001 From: WolfieLeader Date: Thu, 19 Mar 2026 05:14:00 +0200 Subject: [PATCH 10/10] fix: address review feedback on AI context files - Remove auth-schema.ts gotcha (doesn't exist in arktype repo) - Set cursor rules globs to "**/*.ts" so rule auto-activates - Wrap default operator example in object context - Add sync comment noting overlapping content locations --- .cursor/rules/arktype.mdc | 4 ++-- CLAUDE.md | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.cursor/rules/arktype.mdc b/.cursor/rules/arktype.mdc index 5586a1cf85..9f54c824a1 100644 --- a/.cursor/rules/arktype.mdc +++ b/.cursor/rules/arktype.mdc @@ -1,6 +1,6 @@ --- description: When working with ArkType for schema validation. Covers common patterns, pitfalls, and idiomatic usage. -globs: +globs: "**/*.ts" alwaysApply: false --- @@ -93,7 +93,7 @@ type("string & /@pattern/") // intersection type("number > 0") // exclusive min type("number >= 0") // inclusive min type("string <= 255") // max length -type("string = 'default'") // default value +type({ name: "string = 'default'" }) // default value ``` ## Morphs diff --git a/CLAUDE.md b/CLAUDE.md index 69a89e8467..ce96b9b19f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -80,5 +80,6 @@ type("string").or("number") // chained - `"string | undefined"` for optional — use `"key?": "string"` instead - `"Record"` doesn't work — use `type({ "[string]": T })` - `safeParse()` doesn't exist — call the type directly: `const out = Schema(data)` -- `auth-schema.ts` is generated — never edit it - `regexStringNode()` patterns must be anchored with `^`/`$` + +