// Sheets · Apps Script

Use default parameters instead of undefined checks.

Default parameters in Google Apps Script replace the old typeof-undefined guard pattern — but they only fire on undefined, not on null or the empty string that Sheets passes for blank cells. Learn how to handle both cases correctly in custom functions.

I want to clean up my Apps Script functions by using default parameters instead of writing typeof checks at the top of every function, but I'm not sure why my defaults don't seem to work when called from a spreadsheet cell.

The script

copy · paste · trigger
defaultParams.gs
Apps Script
// Custom function: formats a number with a given prefix and decimal places.
// Empty cells arrive as "" from Sheets — not undefined — so defaults alone aren't enough.
function formatValue(value, prefix, decimals) {
  prefix   = (prefix   === undefined || prefix   === '') ? '$'  : prefix;
  decimals = (decimals === undefined || decimals === '') ? 2    : Number(decimals);

  if (typeof value !== 'number') {
    return 'N/A';
  }

  return prefix + value.toFixed(decimals);
}

// Pure-script helper — no Sheets input, so a true ES6 default is safe here.
function buildLabel(name, separator) {
  if (separator === undefined) separator = ' - ';
  return name + separator + 'report';
}

Need a variant? Gnaw writes a custom version from one sentence — fields, triggers, edge cases handled.

Walkthrough

What the old typeof dance was solving

Before ES6 reached Apps Script's V8 runtime (which happened in 2020), the only way to set a fallback value for an optional parameter was a guard at the top of the function body: `if (typeof prefix === 'undefined') prefix = '$';`. The `typeof` form was idiomatic because accessing an undeclared variable directly throws a ReferenceError, while `typeof undeclaredVar` safely returns the string `'undefined'`. For declared parameters, a simple `=== undefined` check is fine — the ReferenceError can't happen — but muscle memory kept the `typeof` form alive long after it was necessary.

With V8, you can write `function formatValue(value, prefix = '$', decimals = 2)` and skip the guard entirely. The runtime assigns the default when the argument is `undefined`: when the caller omits the argument, or explicitly passes `undefined`. That covers every internal call you make from other script functions.

Why Sheets breaks the clean version

Custom functions receive their arguments from cell values, and Sheets serialises a blank cell as an empty string, not `undefined`. So if B2 is empty and your formula is `=FORMATVALUE(A2, B2, C2)`, the `prefix` parameter arrives as `''`. A default parameter never fires — `''` is not `undefined` — so your function gets the empty string and produces output like `42.00` with no prefix at all, silently wrong.

The fix is to treat `''` as a missing value at the point of assignment. You cannot do this with a default parameter expression alone; default parameter expressions only run on `undefined`. Instead, reassign inside the function body: `prefix = (prefix === undefined || prefix === '') ? '$' : prefix;`. It is two characters longer than the ES6 form. The trade is worth it: the function now behaves the same whether called from the script editor, a unit test, or a spreadsheet cell.

I keep a small utility for this in a shared `utils.gs` file: `function coerce(val, fallback) { return (val === undefined || val === '') ? fallback : val; }`. After the first time a blank-cell bug burned thirty minutes of debugging, it seemed worth the eight lines.

When the ES6 default is actually safe

If a function is only ever called from other script code — menu handlers, triggers, library functions, Sheets API calls where you control the arguments — a true ES6 default parameter is correct and clean. There is no blank-cell serialisation happening. The `buildLabel` example in the snippet is that case: it is a helper called by other functions, never by a spreadsheet formula.

The practical rule: if the function has a `@customfunction` JSDoc tag, or if it could be called via `=MYFUNCTION()` by a user, defend against `''` explicitly. If it is an internal helper, let V8 handle it with a real default. Mixing both patterns in the same codebase is fine as long as the distinction is intentional.

Want a custom version?

Describe your sheet and the rule you want. Gnaw writes the Apps Script — fields, triggers, edge cases — in one shot.

FAQ

4 questions
Do ES6 default parameters work in Apps Script?
Yes, since the V8 runtime became the default in 2020. The legacy Rhino runtime did not support them, but Rhino is effectively deprecated. If your script was created recently, V8 is what you have.
Why doesn't my default parameter trigger when a cell is empty?
Sheets passes an empty string for blank cells, not undefined. Default parameters only fire on undefined. Add an explicit empty-string check alongside the undefined check, or use a coerce helper that treats both the same.
Can I use a default parameter to catch null values too?
Not automatically. Default parameters fire only on undefined, not on null. If your data might include null — common when reading from external APIs or certain Sheets ranges — add `|| val === null` to your coerce condition.
Is there a performance cost to checking both undefined and empty string on every call?
No measurable one. The check is a single equality comparison executed once at function entry. Sheets custom function overhead is dominated by the quota system and serialisation round-trips, not by a two-condition guard.