---
title: "Nothing is null: ?T"
image: "https://hem.run/social/hem.png"
---

> Documentation Index
> Fetch the complete documentation index at: https://hem.run/llms.txt
> Use this file to discover all available pages before exploring further.

# Nothing is null: ?T

Use `?T` for everyday absence in Hem-owned application models, and `None` to construct an absent value. Handle it with matching, defaulting, or a checked binding. You do not need to choose a JavaScript sentinel for each new application field.

`?T` means `T | null | undefined`, unboxed. It gives both values the same absence-handling operations; it does not convert existing values to one runtime representation.

Hem provides five ways to eliminate a `?T` value:

```hem
match cache.get(key) {                 // 1. match — both cases, checked
| Some(hit) => hit
| None => recompute(key)
}

let title = doc?.head?.title ?? "untitled"   // 2 & 3. optional chaining, defaulting

let Some(user) = findUser(id) else return notFound()   // 4. let-else guard clause

if el != None { el.focus() }           // 5. comparison — tsc narrows `el` to HTMLElement
```

Optional calls are also supported: `onProgress?.(update)` emits an explicit nullish check rather than a truthiness check, yielding `R | undefined` for an `R`-returning callback.

`Some` and `None` are **type-directed**. As patterns on nullish values they lower to nullish checks; as expressions in a nullish context, `Some(e)` is just `e` and `None` emits `null` or `undefined` by the target type:

```hem
node.textContent = None                // → null — the DOM setter takes string | null
let found: ?Element = None             // → undefined
```

Comparisons are the one place Hem emits a loose check on purpose: `x != None` compiles to `x != null`, the TypeScript idiom that eliminates `null` and `undefined` together in a single evaluation, which is exactly what "not `None`" means.

The `let`-`else` refutable pattern set also heads a loop: `while let Some(line) = input.next() { … }` re-tests it each iteration and stops when it fails (§10).

Use `?T` for ordinary unboxed absence. Choose boxed `Option<T>` when you need the distinct states described below; preserve exact types when an external contract requires them. Bare `Some(x)` refuses with HEM2025; bare `None` infers undefined. On canonical Option, qualified `Option.some(value)` and `Option.none` make the boxed representation explicit. Lexically bound constructor names keep their ordinary meaning.

Because `?T` is unboxed, generic instantiation collapses; `Map<string, ?number>.get` cannot distinguish an absent key from an entry whose value is `undefined`. In a nullish context `Some(e)` does not establish presence: a nullish payload remains absent. Use boxed `Option<Option<T>>` when outer absence, a present inner absence, and a present value must remain distinct.

### Preserve an external contract

Foreign APIs can distinguish the two JavaScript absence values: `document.getElementById` returns `Element | null`, while `map.get(k)` returns `V | undefined`. Hem retains those types. When implementing such a contract, write the exact union or reuse the library's exported type. Replacing either with `?T` widens the public API. Exact unions are available throughout Hem; using them for interop is authoring guidance, not a compiler restriction.

`None` constructs absence in the expected type. Changing an annotation from `T | null` to `?T` can therefore change its emitted value from `null` to `undefined`. Assigning or propagating an existing value preserves its sentinel. See the [construction and refactoring example](https://hem.run/reference/absence#changing-an-annotation-can-change-construction) before changing an externally visible contract.

The rest of the rules live in the [absence reference](https://hem.run/reference/absence): exactly how `None` picks its emit, the evaluation order of an optional call, the refused `??T`, and the exact boundary contract rule.

Source: https://hem.run/tour/nothing-is-null-t/index.mdx
