From 41dae20a284f93567e03b4244eeb104d6b4508c3 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 17 May 2023 12:08:41 +0900 Subject: [PATCH] Additional Fast Path Undefined Checks (#439) --- package-lock.json | 4 ++-- package.json | 2 +- src/typebox.ts | 8 ++++++++ src/value/check.ts | 4 ++-- test/runtime/compiler/object.ts | 27 +++++++++++++++++++++++++++ 5 files changed, 40 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 640ff4d..5e568c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.28.10", + "version": "0.28.11", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.28.10", + "version": "0.28.11", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 07cc9d4..f889ab9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.28.10", + "version": "0.28.11", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index 6f2bc1b..a6c405a 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -1353,6 +1353,14 @@ export namespace TypeGuard { export namespace ExtendsUndefined { export function Check(schema: TSchema): boolean { if (schema[Kind] === 'Undefined') return true + if (schema[Kind] === 'Not') { + const not = schema as TNot + return Check(not.allOf[1]) + } + if (schema[Kind] === 'Intersect') { + const intersect = schema as TIntersect + return intersect.allOf.every((schema) => Check(schema)) + } if (schema[Kind] === 'Union') { const union = schema as TUnion return union.anyOf.some((schema) => Check(schema)) diff --git a/src/value/check.ts b/src/value/check.ts index 9b62e00..0700dbd 100644 --- a/src/value/check.ts +++ b/src/value/check.ts @@ -238,8 +238,8 @@ export namespace ValueCheck { if (!Visit(property, references, value[knownKey])) { return false } - if (Types.ExtendsUndefined.Check(property)) { - return knownKey in value + if (Types.ExtendsUndefined.Check(property) && !(knownKey in value)) { + return false } } else { if (IsExactOptionalProperty(value, knownKey) && !Visit(property, references, value[knownKey])) { diff --git a/test/runtime/compiler/object.ts b/test/runtime/compiler/object.ts index f1299f7..872b987 100644 --- a/test/runtime/compiler/object.ts +++ b/test/runtime/compiler/object.ts @@ -2,6 +2,33 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' describe('type/compiler/Object', () => { + // ----------------------------------------------------- + // TypeCompiler Only + // ----------------------------------------------------- + it('Should handle extends undefined check 1', () => { + const T = Type.Object({ + A: Type.Not(Type.Number(), Type.Undefined()), + B: Type.Union([Type.Number(), Type.Undefined()]), + C: Type.Intersect([Type.Undefined(), Type.Undefined()]), + }) + Ok(T, { + A: undefined, + B: undefined, + C: undefined, + }) + }) + // https://github.com/sinclairzx81/typebox/issues/437 + it('Should handle extends undefined check 2', () => { + const T = Type.Object({ + A: Type.Not(Type.Null(), Type.Undefined()), + }) + Ok(T, { A: undefined }) + Fail(T, { A: null }) + Fail(T, {}) + }) + // ----------------------------------------------------- + // Standard Checks + // ----------------------------------------------------- it('Should not validate a number', () => { const T = Type.Object({}) Fail(T, 42)