typebox 1.0.8 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ export declare function IsExactOptional(required: string[], key: string): boolean;
2
+ export declare function InexactOptionalBuild(value: string, key: string): string;
3
+ export declare function InexactOptionalCheck(value: Record<PropertyKey, unknown>, key: string): boolean;
@@ -0,0 +1,20 @@
1
+ import { Settings } from '../../system/settings/index.mjs';
2
+ import { EmitGuard as E, Guard as G } from '../../guard/index.mjs';
3
+ // ------------------------------------------------------------------
4
+ // IsExactOptional
5
+ // ------------------------------------------------------------------
6
+ export function IsExactOptional(required, key) {
7
+ return required.includes(key) || Settings.Get().exactOptionalPropertyTypes;
8
+ }
9
+ // ------------------------------------------------------------------
10
+ // ExactOptionalBuild
11
+ // ------------------------------------------------------------------
12
+ export function InexactOptionalBuild(value, key) {
13
+ return E.IsUndefined(E.Member(value, key));
14
+ }
15
+ // ------------------------------------------------------------------
16
+ // ExactOptionalCheck
17
+ // ------------------------------------------------------------------
18
+ export function InexactOptionalCheck(value, key) {
19
+ return G.IsUndefined(value[key]);
20
+ }
@@ -2,6 +2,7 @@
2
2
  import { Guard as G, EmitGuard as E } from '../../guard/index.mjs';
3
3
  import * as S from '../types/index.mjs';
4
4
  import { BuildSchema, CheckSchema, ErrorSchema } from './schema.mjs';
5
+ import { InexactOptionalCheck, InexactOptionalBuild, IsExactOptional } from './_exact_optional.mjs';
5
6
  // ------------------------------------------------------------------
6
7
  // Build
7
8
  // ------------------------------------------------------------------
@@ -11,9 +12,35 @@ export function BuildProperties(context, schema, value) {
11
12
  const notKey = E.Not(E.HasPropertyKey(value, E.Constant(key)));
12
13
  const isSchema = BuildSchema(context, schema, E.Member(value, key));
13
14
  const addKey = context.AddKey(E.Constant(key));
14
- // optimization: E.Or(notKey, E.And(isSchema, addKey))
15
15
  const guarded = context.UseUnevaluated() ? E.And(isSchema, addKey) : isSchema;
16
- return !required.includes(key) ? E.Or(notKey, guarded) : guarded;
16
+ // --------------------------------------------------------------
17
+ // Optimization
18
+ //
19
+ // If a key is required, we can skip the `notKey` check since this
20
+ // condition is already enforced by Required. This optimization is
21
+ // only valid when Required is evaluated before Properties.
22
+ //
23
+ // --------------------------------------------------------------
24
+ const isProperty = required.includes(key) ? guarded : E.Or(notKey, guarded);
25
+ // --------------------------------------------------------------
26
+ // ExactOptionalProperties
27
+ //
28
+ // By default, TypeScript allows optional properties to be assigned
29
+ // undefined. This is a bit misleading, since 'optional' is usually
30
+ // understood to mean 'the key may be absent', not 'the key may be
31
+ // present with an undefined value'.
32
+ //
33
+ // The 'IsExactOptional' check returns false by default, matching
34
+ // TypeScript's behavior. When exactOptionalPropertyTypes is enabled
35
+ // in tsconfig.json, TypeBox can be configured to use the stricter
36
+ // semantics via System settings:
37
+ //
38
+ // Settings.Set({ exactOptionalPropertyTypes: true })
39
+ //
40
+ // --------------------------------------------------------------
41
+ return IsExactOptional(required, key)
42
+ ? isProperty
43
+ : E.Or(InexactOptionalBuild(value, key), isProperty);
17
44
  });
18
45
  return E.ReduceAnd(everyKey);
19
46
  }
@@ -21,9 +48,12 @@ export function BuildProperties(context, schema, value) {
21
48
  // Check
22
49
  // ------------------------------------------------------------------
23
50
  export function CheckProperties(context, schema, value) {
51
+ const required = S.IsRequired(schema) ? schema.required : [];
24
52
  const isProperties = G.Every(G.Entries(schema.properties), ([key, schema]) => {
25
- return !G.HasPropertyKey(value, key)
26
- || (CheckSchema(context, schema, value[key]) && context.AddKey(key));
53
+ const isProperty = !G.HasPropertyKey(value, key) || (CheckSchema(context, schema, value[key]) && context.AddKey(key));
54
+ return IsExactOptional(required, key)
55
+ ? isProperty
56
+ : InexactOptionalCheck(value, key) || isProperty;
27
57
  });
28
58
  return isProperties;
29
59
  }
@@ -31,11 +61,15 @@ export function CheckProperties(context, schema, value) {
31
61
  // Error
32
62
  // ------------------------------------------------------------------
33
63
  export function ErrorProperties(context, schemaPath, instancePath, schema, value) {
64
+ const required = S.IsRequired(schema) ? schema.required : [];
34
65
  const isProperties = G.EveryAll(G.Entries(schema.properties), ([key, schema]) => {
35
66
  const nextSchemaPath = `${schemaPath}/properties/${key}`;
36
67
  const nextInstancePath = `${instancePath}/${key}`;
37
- return !G.HasPropertyKey(value, key)
38
- || (ErrorSchema(context, nextSchemaPath, nextInstancePath, schema, value[key]) && context.AddKey(key));
68
+ // Defer error generation for IsExactOptional
69
+ const isProperty = () => (!G.HasPropertyKey(value, key) || (ErrorSchema(context, nextSchemaPath, nextInstancePath, schema, value[key]) && context.AddKey(key)));
70
+ return IsExactOptional(required, key)
71
+ ? isProperty()
72
+ : InexactOptionalCheck(value, key) || isProperty();
39
73
  });
40
74
  return isProperties;
41
75
  }
@@ -23,6 +23,16 @@ export interface TSettings {
23
23
  * @default true
24
24
  */
25
25
  useEval: boolean;
26
+ /**
27
+ * Enables or disables 'exactOptionalPropertyTypes' check semantics. By default, TypeScript
28
+ * allows optional properties to be assigned 'undefined'. While this behavior differs from the
29
+ * common interpretation of 'optional' as meaning 'key may be absent', TypeBox adopts the default
30
+ * TypeScript semantics to remain consistent with the language. This option is provided to align
31
+ * runtime check semantics with projects that configure 'exactOptionalPropertyTypes: true' in
32
+ * tsconfig.json.
33
+ * @default false
34
+ */
35
+ exactOptionalPropertyTypes: boolean;
26
36
  /**
27
37
  * Controls whether internal compositor properties (`~kind`, `~readonly`, `~optional`) are enumerable.
28
38
  * @default false
@@ -4,6 +4,7 @@ const settings = {
4
4
  immutableTypes: false,
5
5
  maxErrors: 8,
6
6
  useEval: true,
7
+ exactOptionalPropertyTypes: false,
7
8
  enumerableKind: false
8
9
  };
9
10
  /** Resets system settings to defaults */
@@ -11,6 +12,7 @@ export function Reset() {
11
12
  settings.immutableTypes = false;
12
13
  settings.maxErrors = 8;
13
14
  settings.useEval = true;
15
+ settings.exactOptionalPropertyTypes = false;
14
16
  settings.enumerableKind = false;
15
17
  }
16
18
  /** Sets system settings */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "typebox",
3
3
  "description": "A Runtime Type System for JavaScript",
4
- "version": "1.0.8",
4
+ "version": "1.0.9",
5
5
  "keywords": [
6
6
  "typescript",
7
7
  "jsonschema"