Direct answer
No. The CoffeeScript 2 compiler cannot be configured to emit fallback-safe code instead of native `?.`. It lowers the existential operator directly to JavaScript optional chaining, and there is no compiler flag such as `--no-optional-chaining` to restore CoffeeScript 1.x behavior. The supported way to support runtimes without `?.` is to transpile the emitted JavaScript afterward with Babel, swc, or esbuild.
Confirmed facts vs. likely explanation
Confirmed: CoffeeScript 2 aligns `?` with ES2020 optional chaining. Engines without native support will throw `TypeError` on `a?.b` where CoffeeScript 1 returned `undefined`.
Likely explanation: the compiler deliberately defers downleveling to the broader JavaScript toolchain rather than bundling its own legacy emitter. That means bundle size and error propagation depend on your transpiler configuration, not on a CoffeeScript option.
Patterns to audit
- `receiver?.property` — optional property access.
- `receiver?.[index]` — optional indexing.
- `receiver?.()` — optional call.
The unary existential `receiver?` compiles to `receiver != null ? receiver : undefined` and is safe without transpilation. Only chained forms are affected. Side-effecting receivers such as `fn()?.prop` deserve extra attention because some transpilers may evaluate the receiver more than once.
Steps for this case
- Run `coffee --compile --print file.coffee` on a sample file and confirm the output contains literal `?.` tokens.
- If your build already uses Babel, swc, or esbuild, configure it to downlevel optional chaining for your oldest target. Example Babel step:
npx babel --plugins=@babel/plugin-transform-optional-chaining compiled.js
- If you do not use a transpiler, add one after CoffeeScript compilation and point `browserslist` or Babel `targets` at the legacy environments you must support.
- Execute the transpiled output in a legacy runtime such as Node 12 and verify that missing receivers return `undefined` instead of throwing.
Migration strategy
There is no documented CoffeeScript-specific migration guide that preserves the original silent-undefined semantics without a transpilation step. Treat the CoffeeScript compiler as an ES2020 emitter and handle legacy support in the JavaScript build stage.
Assumption and uncertainty: this answer treats the CoffeeScript 2 changelog and README descriptions as planning context, not live-verified evidence. Exact plugin names, bundle-size impact, and transpiler behavior should be confirmed in your own toolchain before deployment.
Missing diagnostic detail: do you already run compiled JavaScript through Babel, swc, or esbuild? That determines whether you only need to adjust targets or must add a new build step.