How to say a question mark for optionals and optional chaining
Say “question mark” to produce a question mark bound to the token before it, so “name question mark colon string” assembles as name?: string. The same phrase followed by “dot” gives optional chaining — value?.name — because the question mark attaches left and the period attaches on both sides.
A twelve-word optional chain runs about 14 seconds typed against about 4.4 seconds spoken. (sources: Dhakal, Feit, Kristensson & Oulasvirta, “Observations on Typing from 136 Million Keystrokes”, CHI 2018, 168,000 participants, 136 million keystrokes; Yuan, Liberman & Cieri, “Towards an Integrated Understanding of Speaking Rate in Conversation”, Interspeech 2006, 2,438 American English telephone conversations (Switchboard corpus). The speech figure is conversational rate, not a measured dictation rate — no such measurement exists.)
userProfile?.display name?.trimThis tool runs entirely in your browser. Nothing you type is sent anywhere, stored, or logged.
Step by step
- 1
Say the identifier, then “question mark”
Speak the name followed by the phrase. The question mark binds left, so it lands flush against the identifier and marks it optional without a gap.
- 2
Add “dot” for optional chaining
Say “question mark dot” between links. The two characters combine into ?. because VoiceGem's period attaches on both sides, and the chain reads exactly as written.
- 3
Use “colon” for the ternary branch
Say “question mark”, the true branch, “colon”, then the false branch. Both punctuation marks attach left, so the expression comes out spaced like source.
- 4
Typing it instead
Press Shift-slash. Swift and TypeScript developers hit this chord constantly, and for a single optional marker it is faster than saying three syllables.
Optional chaining falls out of two attach flags rather than a special case
There is no entry in VoiceGem's table for ?. as a unit. The sequence appears because the question mark attaches to its left and the period attaches on both sides, so two independent lookups produce a chaining operator with nothing between them.
Composition rather than enumeration is what keeps the table at forty symbols instead of several hundred. The Deterministic Pass never needs an entry for every operator pair, because the per-character flags already describe how characters join — which is also why the same two phrases work in Swift, TypeScript, JavaScript, C# and Kotlin without a language setting.
Where the manual way breaks
A single question mark is one shifted keystroke and nobody needs help with it. For a lone optional annotation, typing wins clearly.
Deep optional chains are the case that hurts to type. Three links of ?. with a call at the end is a run of shifted characters interleaved with identifiers, and it is the kind of line where a missed marker produces a runtime error rather than a compile error. Saying the chain gives you every marker in the same place every time, and VoiceGem's output does not vary between runs the way a model-formatted line can.
Doing this somewhere else? the same speech-to-code converter this demo runs, the backslash in an escape sequence specifically, what changes for the colon in a type annotation, or what changes for the comma in an argument list.
Common questions
Say “question mark dot” between the two identifiers. VoiceGem produces ?. from two separate table entries, because the question mark binds left and the period binds on both sides.
Yes. Say the type then “question mark”, as in “string question mark”. The character lands flush against the type name with no space in front of it.
Yes. Say “question mark”, the first branch, “colon”, then the second. Both marks attach left, so the result comes out spaced the way a formatter leaves a ternary.
Dictate a three-link optional chain and check every marker.
Open the tool