From 592787a35349c6b1c0011ae80220efed4885f340 Mon Sep 17 00:00:00 2001 From: Avanish Gupta Date: Tue, 27 Jan 2026 22:13:08 +0530 Subject: [PATCH 1/2] feat: add unevaluatedItems lesson for closing tuples (#61) This lesson demonstrates the proper use case for unevaluatedItems: - Closing a tuple defined in a referenced schema using $ref - Why items cannot achieve this with schema composition - The difference between items and unevaluatedItems Example: A coordinate tuple (lat, long) that needs to be closed to prevent extra items like altitude. Fixes #61 --- .../code.ts | 69 +++++++++++++++++++ .../instructions.mdx | 65 +++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 content/07-Miscellaneous/02-Extending-Closed-Tuples-with-unevaluatedItems/code.ts create mode 100644 content/07-Miscellaneous/02-Extending-Closed-Tuples-with-unevaluatedItems/instructions.mdx diff --git a/content/07-Miscellaneous/02-Extending-Closed-Tuples-with-unevaluatedItems/code.ts b/content/07-Miscellaneous/02-Extending-Closed-Tuples-with-unevaluatedItems/code.ts new file mode 100644 index 0000000..750942d --- /dev/null +++ b/content/07-Miscellaneous/02-Extending-Closed-Tuples-with-unevaluatedItems/code.ts @@ -0,0 +1,69 @@ +const code: any = { + $ref: "#/$defs/coordinate", + $defs: { + coordinate: { + type: "array", + prefixItems: [ + { type: "number", description: "latitude" }, + { type: "number", description: "longitude" }, + ], + }, + }, +}; + +const solution = structuredClone(code); +solution.unevaluatedItems = false; + +const testCases = [ + // Valid: exactly two numbers (lat, long) + { + input: [40.7128, -74.006], + expected: true, + }, + // Valid: different coordinates + { + input: [51.5074, -0.1278], + expected: true, + }, + // Valid: negative coordinates + { + input: [-33.8688, 151.2093], + expected: true, + }, + // Valid: zero coordinates (null island) + { + input: [0, 0], + expected: true, + }, + // Invalid: extra item (altitude) - this is what unevaluatedItems: false prevents + { + input: [40.7128, -74.006, 10], + expected: false, + }, + // Invalid: extra string item + { + input: [40.7128, -74.006, "NYC"], + expected: false, + }, + // Invalid: multiple extra items + { + input: [40.7128, -74.006, 10, "NYC", true], + expected: false, + }, + // Invalid: wrong type for latitude + { + input: ["forty", -74.006], + expected: false, + }, + // Invalid: wrong type for longitude + { + input: [40.7128, "west"], + expected: false, + }, +]; + +module.exports = { + code, + solution, + testCases, +}; diff --git a/content/07-Miscellaneous/02-Extending-Closed-Tuples-with-unevaluatedItems/instructions.mdx b/content/07-Miscellaneous/02-Extending-Closed-Tuples-with-unevaluatedItems/instructions.mdx new file mode 100644 index 0000000..be734b0 --- /dev/null +++ b/content/07-Miscellaneous/02-Extending-Closed-Tuples-with-unevaluatedItems/instructions.mdx @@ -0,0 +1,65 @@ +--- +title: Closing Tuples with unevaluatedItems +description: "Learn how to close tuple arrays in JSON Schema using the unevaluatedItems keyword when working with schema composition like $ref." +keywords: "closing tuples, unevaluatedItems, JSON Schema, arrays, tuples, prefixItems, $ref, schema composition" +--- + +# Closing Tuples with unevaluatedItems + +In the Arrays module, we learned about using `items: false` to close a tuple and prevent additional items. However, `items` only recognizes `prefixItems` declared in the same [subschema](https://json-schema.org/learn/glossary#subschema) as itself. + +This becomes a problem when you want to close a tuple that's defined in a referenced schema using `$ref`. + +## The Problem with items + +Consider a coordinate tuple defined in `$defs` that we want to reference and close: + +```json +{ + "$ref": "#/$defs/coordinate", + "items": false, + "$defs": { + "coordinate": { + "type": "array", + "prefixItems": [ + { "type": "number" }, + { "type": "number" } + ] + } + } +} +``` + +This **will not work as expected**. The `items: false` keyword only sees `prefixItems` in its own subschema (there are none), so it would reject ALL items, making the schema unusable. + +## The Solution: unevaluatedItems + +The `unevaluatedItems` keyword solves this problem. It considers all `prefixItems` across the entire schema, including those in referenced schemas. + +```json highlightLineStart={3} +{ + "$ref": "#/$defs/coordinate", + "unevaluatedItems": false, + "$defs": { + "coordinate": { + "type": "array", + "prefixItems": [ + { "type": "number" }, + { "type": "number" } + ] + } + } +} +``` + +Now the schema correctly: +- Allows exactly two number items (from the referenced coordinate tuple) +- Rejects any additional items beyond the defined tuple + +This is the key difference: `unevaluatedItems` looks at what items have been "evaluated" by any part of the schema, while `items` only looks at the local `prefixItems`. + +## Task + +You are given a coordinate tuple schema that defines latitude and longitude. Your task is to **close the tuple** so that no additional items can be added beyond the two coordinates. + +> **Hint:** Add `unevaluatedItems` set to `false` to prevent any extra items in the array. From c3755b442324187c9b2061db8080368cbaacb30b Mon Sep 17 00:00:00 2001 From: Avanish Gupta Date: Thu, 29 Jan 2026 12:41:05 +0530 Subject: [PATCH 2/2] Issue #61: clarify unevaluatedItems wording --- .../instructions.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/07-Miscellaneous/02-Extending-Closed-Tuples-with-unevaluatedItems/instructions.mdx b/content/07-Miscellaneous/02-Extending-Closed-Tuples-with-unevaluatedItems/instructions.mdx index be734b0..c8309f7 100644 --- a/content/07-Miscellaneous/02-Extending-Closed-Tuples-with-unevaluatedItems/instructions.mdx +++ b/content/07-Miscellaneous/02-Extending-Closed-Tuples-with-unevaluatedItems/instructions.mdx @@ -34,7 +34,7 @@ This **will not work as expected**. The `items: false` keyword only sees `prefix ## The Solution: unevaluatedItems -The `unevaluatedItems` keyword solves this problem. It considers all `prefixItems` across the entire schema, including those in referenced schemas. +The `unevaluatedItems` keyword solves this problem. It considers annotations produced by subschemas in its evaluation scope. In this example, the `$ref` and `unevaluatedItems` are in the same schema object, so the tuple items evaluated by the referenced schema are visible to `unevaluatedItems`. ```json highlightLineStart={3} { @@ -56,7 +56,7 @@ Now the schema correctly: - Allows exactly two number items (from the referenced coordinate tuple) - Rejects any additional items beyond the defined tuple -This is the key difference: `unevaluatedItems` looks at what items have been "evaluated" by any part of the schema, while `items` only looks at the local `prefixItems`. +This is the key difference: `unevaluatedItems` only uses annotations from the same evaluation path (roughly, subschemas "below" it). If the tuple is evaluated in a different branch (for example, inside an `allOf` alongside `unevaluatedItems`), it won't see those `prefixItems`. ## Task