Optional Types
The `?` suffix, and how it desugars to a union with `none`.
An optional type says "this is a T, or it isn't there at all." Raven writes this with a trailing ? on any type.
Syntax
let nickname: string? = none
let age: number? = 30string? reads as "a string, or none." Both a real value and none satisfy it.
? is sugar, not a separate mechanism
Under the hood, T? is exactly T | none — there's no distinct "optional" concept the checker treats specially beyond that. Writing string? and writing string | none produce the identical type. This matters because it means everything covered in Union Types — how variants get compared, how assignability works — applies here too, for free.
? can trail any type atom, not just primitives — including a literal, a record, or even a union member inside a larger union:
let role: "admin"? = none // "admin" | none
let user: { name: string }? = none // { name: string } | none
let mixed: string? | number = "x" // (string | none) | numberOptional record fields
This is where ? shows up most naturally in practice — describing a field that might be absent:
model User = { name: string, nickname: string? }
let a = { name: "Ada" } // fine — nickname is optional
let b = { name: "Ada", nickname: "Ace" } // also fineA field typed as optional in a target annotation doesn't have to be present in the source at all:
let user: { name: string, age: number? } = { name: "Ada" }
// fine — age is missing entirely, but it's optional, so that's allowedThis is also how Raven automatically infers optional fields when merging record shapes — covered in Arrays + Records.
Assignability rules
- A plain
Tis assignable into aT?slot — you don't need to explicitly writenonefor the non-nonecase to work. noneis assignable into anyT?slot, for anyT.- A
T?is not assignable into a plainTslot — the checker won't let you drop the possibility ofnonesilently. If you have astring?and need astring, you have to narrow it yourself (Raven doesn't currently have flow-sensitive narrowing from anifcheck).
let maybe: string? = "hi"
let definite: string = maybe
// Type mismatch: expected 'string', but got 'string?'What's next
Optional types are the single most common union you'll write, but the underlying mechanism — normalization, deduplication, how any interacts with a union — is the full subject of Union Types.