CoffeeScript ? vs ?.: Null-Safety Operators and Compilation Quirks
CoffeeScript's ? and ?. offer concise null-safety, but their distinct return values and compilation paths require careful attention when targeting different JavaScript runtimes.
01 Feb 2026, 20:41 UTC

The problem with null in chained access
In JavaScript, pulling a property deep inside an object graph often triggers a TypeError if any intermediate value is null or undefined. Defending against that requires either defensive if statements or the newer optional chaining syntax. CoffeeScript provided these patterns early, but the behavior of its two primary null-safety operators—the existential operator and the safe navigation operator—differs in ways that can cause subtle bugs if misunderstood.
The existential operator ?
The existential operator ? returns the operand's value if it is not null or undefined; otherwise, it returns false. This makes it a powerful tool for guard clauses and predicate-style checks without needing a full if block.
# Guarding a variable
result = user? # false if user is null/undefined, otherwise the user objectThe key distinction here is the return value. Because ? returns false rather than undefined, it is specifically designed for boolean contexts. If your downstream code strictly checks for undefined, using the existential operator will change the logic of your application.
Safe navigation ?. and comprehensions
The safe navigation operator ?. allows you to access properties or invoke methods without manually guarding every step of the chain. If the receiver is null or undefined, the expression short-circuits and returns undefined, allowing the rest of the chain to be skipped safely.
# Safe property access
birthday = user?.profile?.birthday # undefined if user or profile is missingIn CoffeeScript 2, ?. is refined to align with modern JavaScript's optional chaining. When compiled, it typically maps directly to the JavaScript ?. operator. However, if you are targeting older runtimes (ES5), the transpiler must convert this into a series of ternary checks or a shim, which increases the size of the generated code.
These operators are particularly useful within CoffeeScript comprehensions, which provide a declarative way to map and filter arrays. By combining ?. with a when clause, you can filter nullable data concisely:
# Collect names of users who are over 18, safely handling missing age properties
adultNames = [user.name for user of users when user?.age > 18]This compiles to an idiomatic JavaScript pattern (usually a filter followed by a map), ensuring there is no heavy runtime overhead while maintaining safety.
Trade-offs and verification
The primary risk is treating ? and ?. as interchangeable. The existential ? is for boolean-like guards (returning false), while the safe navigator ?. is for value retrieval (returning undefined). Using the wrong one can lead to logic errors in strict equality checks (===).
To verify the behavior in your specific environment:
- Create a
.coffeefile with test cases:val1 = (null)?andval2 = (null)?.prop. - Run
coffee -c filename.coffeefrom your terminal (requires the CoffeeScript compiler installed; requires write permissions for the output.jsfile). - Inspect the resulting JavaScript to see if
?.was compiled to native optional chaining or a polyfill. - Execute the code in your target runtime and assert that
val1isfalseandval2isundefined.
If you are deploying to a legacy environment that does not support ES2020, ensure your build pipeline includes a downshimming step for optional chaining to avoid syntax errors in production.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.