Absence: option

Status: implemented

option admits absence: the member that is not there. It is a nullary schema like boolean, and a member that may be omitted says so by union.

{ a: or(option, number) }             // `a` may be absent, or a number
{ a: or(number, undefined) }          // `a` must be present, may hold `undefined`
{ a: or(option, number, undefined) }  // absent, a number, or a present `undefined`
[3, or(option, number)]               // position 1 may be absent: `[3]`

Absence is not a spelling of the value undefined: {} and { a: undefined } are two distinct values, and only a set admitting absence accepts the first. A member is required exactly when its set excludes absence, so a struct key or a tuple position without option must be there (structs and tuples are closed).

Absence is observable only at a container position. No caller can hand a reader an argument that is not there, so a top-level schema admitting absence accepts exactly what the rest of its union accepts. A tuple position is absent only past the end of the array, since FunctionalScript builds no array with a hole and the readers read none (what the readers assume of a value). A container's rest never sees an absent member either.

Neither reader fills an absent member in: parse({ a: or(option, number) })({}) builds {}, with no key a, and validate hands back the {} it was given.

Examples

The demo on this page runs the Absent vs undefined lesson: {} is accepted by { a: or(option, number) } and refused by { a: or(number, undefined) }. Type export default {"a":undefined}; to see the answers swap. The lesson is defined in sample.f.mjs, and the module page's demo lists it among all the others.

Reference

  • TypeScript: optional properties. Ts<> renders or(option, t) as an optional key, a?: Ts<t>; the difference from undefined is the one exactOptionalPropertyTypes draws.
  • JSON Schema: a key admitting absence is left out of required, and a tuple position out of minItems (object)

Where it is implemented

option in module.f.mjs; the absent bit of a member's unit set in data.

Demo

or(option, t) lets the key be left out. or(t, undefined) needs the key, though its value may be undefined: try export default {"a":undefined};

{} [a] → or{}aoroptionnumber

TypeScript:

{readonly"a"?:number}

JSON Schema:

{"type":"object","properties":{"a":{"type":"number"}},"additionalProperties":{"not":{}}}
Canonical form (toData)
export default [{},{"object":[{"props":{"a":{"unit":16,"number":true}},"rest":{}}]}];

Neither is shown to include the other.

equal → false
subset(this)(that) → false
subset(that)(this) → false

parse · ok

export default {};

validate · ok

export default {};

Emergent Testing

Idle. Press Run to start the suite.

  • ./proof.f.mjs