diff --git a/package-lock.json b/package-lock.json index 88a7f79c7..849e6bbd8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.16", + "version": "0.31.17", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.16", + "version": "0.31.17", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index fe958029c..198997ca2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.16", + "version": "0.31.17", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 0c9d2b958..9e840ca92 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -204,7 +204,7 @@ export namespace TypeCompiler { const checkExpression = CreateExpression(containsSchema, references, 'value') const checkMinContains = IsNumber(schema.minContains) ? [`(count >= ${schema.minContains})`] : [] const checkMaxContains = IsNumber(schema.maxContains) ? [`(count <= ${schema.maxContains})`] : [] - const checkCount = `const count = ${value}.reduce((${accumulator}, ${parameter}) => ${checkExpression} ? acc + 1 : acc, 0)` + const checkCount = `const count = value.reduce((${accumulator}, ${parameter}) => ${checkExpression} ? acc + 1 : acc, 0)` const check = [`(count > 0)`, ...checkMinContains, ...checkMaxContains].join(' && ') yield `((${parameter}) => { ${checkCount}; return ${check}})(${value})` } diff --git a/test/runtime/compiler-ajv/array.ts b/test/runtime/compiler-ajv/array.ts index dd9feaea4..589bc03ac 100644 --- a/test/runtime/compiler-ajv/array.ts +++ b/test/runtime/compiler-ajv/array.ts @@ -145,4 +145,42 @@ describe('compiler-ajv/Array', () => { Fail(T, [1, 1, 1, 1, 1]) Fail(T, [1, 1, 1, 1, 1, 1]) }) + // ---------------------------------------------------------------- + // Issue: https://github.com/sinclairzx81/typebox/discussions/607 + // ---------------------------------------------------------------- + it('Should correctly handle undefined array properties', () => { + const Answer = Type.Object({ + text: Type.String(), + isCorrect: Type.Boolean(), + }) + const Question = Type.Object({ + text: Type.String(), + options: Type.Array(Answer, { + minContains: 1, + maxContains: 1, + contains: Type.Object({ + text: Type.String(), + isCorrect: Type.Literal(true), + }), + }), + }) + Fail(Question, { text: 'A' }) + Fail(Question, { text: 'A', options: [] }) + Ok(Question, { text: 'A', options: [{ text: 'A', isCorrect: true }] }) + Ok(Question, { + text: 'A', + options: [ + { text: 'A', isCorrect: true }, + { text: 'B', isCorrect: false }, + ], + }) + Fail(Question, { text: 'A', options: [{ text: 'A', isCorrect: false }] }) + Fail(Question, { + text: 'A', + options: [ + { text: 'A', isCorrect: true }, + { text: 'B', isCorrect: true }, + ], + }) + }) }) diff --git a/test/runtime/compiler/array.ts b/test/runtime/compiler/array.ts index ae8fa4a6a..70af4c500 100644 --- a/test/runtime/compiler/array.ts +++ b/test/runtime/compiler/array.ts @@ -145,4 +145,42 @@ describe('compiler/Array', () => { Fail(T, [1, 1, 1, 1, 1]) Fail(T, [1, 1, 1, 1, 1, 1]) }) + // ---------------------------------------------------------------- + // Issue: https://github.com/sinclairzx81/typebox/discussions/607 + // ---------------------------------------------------------------- + it('Should correctly handle undefined array properties', () => { + const Answer = Type.Object({ + text: Type.String(), + isCorrect: Type.Boolean(), + }) + const Question = Type.Object({ + text: Type.String(), + options: Type.Array(Answer, { + minContains: 1, + maxContains: 1, + contains: Type.Object({ + text: Type.String(), + isCorrect: Type.Literal(true), + }), + }), + }) + Fail(Question, { text: 'A' }) + Fail(Question, { text: 'A', options: [] }) + Ok(Question, { text: 'A', options: [{ text: 'A', isCorrect: true }] }) + Ok(Question, { + text: 'A', + options: [ + { text: 'A', isCorrect: true }, + { text: 'B', isCorrect: false }, + ], + }) + Fail(Question, { text: 'A', options: [{ text: 'A', isCorrect: false }] }) + Fail(Question, { + text: 'A', + options: [ + { text: 'A', isCorrect: true }, + { text: 'B', isCorrect: true }, + ], + }) + }) }) diff --git a/test/runtime/value/check/array.ts b/test/runtime/value/check/array.ts index 421a3e19a..9ad1be58c 100644 --- a/test/runtime/value/check/array.ts +++ b/test/runtime/value/check/array.ts @@ -110,4 +110,46 @@ describe('value/check/Array', () => { Assert.IsFalse(Value.Check(T, [1, 1, 1, 1, 1])) Assert.IsFalse(Value.Check(T, [1, 1, 1, 1, 1, 1])) }) + // ---------------------------------------------------------------- + // Issue: https://github.com/sinclairzx81/typebox/discussions/607 + // ---------------------------------------------------------------- + it('Should correctly handle undefined array properties', () => { + const Answer = Type.Object({ + text: Type.String(), + isCorrect: Type.Boolean(), + }) + const Question = Type.Object({ + text: Type.String(), + options: Type.Array(Answer, { + minContains: 1, + maxContains: 1, + contains: Type.Object({ + text: Type.String(), + isCorrect: Type.Literal(true), + }), + }), + }) + Assert.IsFalse(Value.Check(Question, { text: 'A' })) + Assert.IsFalse(Value.Check(Question, { text: 'A', options: [] })) + Assert.IsTrue(Value.Check(Question, { text: 'A', options: [{ text: 'A', isCorrect: true }] })) + Assert.IsTrue( + Value.Check(Question, { + text: 'A', + options: [ + { text: 'A', isCorrect: true }, + { text: 'B', isCorrect: false }, + ], + }), + ) + Assert.IsFalse(Value.Check(Question, { text: 'A', options: [{ text: 'A', isCorrect: false }] })) + Assert.IsFalse( + Value.Check(Question, { + text: 'A', + options: [ + { text: 'A', isCorrect: true }, + { text: 'B', isCorrect: true }, + ], + }), + ) + }) })