docs/basics/optional_values.md
# Optional / null values
By design, all fields are required, ie. `null` or missing fields will cause a
`TypeError` while reviving. In the case of the `Animal` class from the
[introductory example](../introduction/README.md):
```js
const pet = M.fromJSON(Animal, '{"name": null}')
// => TypeError: no value for key "name"
```
To support missing properties or `null` values, you can either use
`withDefault` or declare the property as a `Maybe`
## Using `withDefault`
The `withDefault` metadata takes some metadata and a default value which will
be used only if the property is `null` or missing.
```js
import M from 'modelico'
const {string, maybe} = M.metadata()
class Animal extends M.Base {
// ... same as before
static innerTypes () {
return Object.freeze({
name: withDefault(string(), 'Unknown')
})
}
}
```
Then, it can be used as any other value:
```js
const pet1 = M.fromJSON(Animal, '{"name": "Bane"}')
const pet2 = M.fromJSON(Animal, '{"name": null}')
const pet3 = M.fromJSON(Animal, '{}')
pet1.name() // => Bane
pet2.name() // => Unknown
pet3.name() // => Unknown
```
## Maybes
```js
import M from 'modelico'
const {string, maybe} = M.metadata()
class Animal extends M.Base {
// ... same as before
static innerTypes () {
return Object.freeze({
name: maybe(string())
})
}
}
```
Then, we can use it as follows:
```js
const pet1 = M.fromJSON(Animal, '{"name": "Bane"}')
// pet2 and pet3 behave the same way
const pet2 = M.fromJSON(Animal, '{"name": null}')
const pet3 = M.fromJSON(Animal, '{}')
pet1.name().isEmpty() // => false
pet1.name().getOrElse('Coco') // => Bane
JSON.stringify(pet1) // => {"name":"Bane"}
pet2.name().isEmpty() // => true
pet2.name().getOrElse('Bane') // => Bane
JSON.stringify(pet2) // => {"name":null}
pet3.name().isEmpty() // => true
pet3.name().getOrElse('Bane') // => Bane
JSON.stringify(pet3) // => {"name":null}
```
_Note: `pet2` does not produce the same JSON it was parsed from. If that is
important to you, one possibility would be to not declare the `name` field and
use `M.fields(pet2).name` to check for its presence and value manually._