aws-appsync-community icon indicating copy to clipboard operation
aws-appsync-community copied to clipboard

odd behavior in js-resolver with nullish coalescing operator ?? and a function call on RHS

Open massfords opened this issue 3 months ago • 1 comments

Given the following:

const result = a ?? b();

The expected behavior in standard JS is that b() is not invoked if a is non-null and non-undefined.

The behavior I'm observing is that b() is invoked, but the assignment to result is the value of a.

This suggests to me that both the LHS and RHS are being evaluated, but the assignment is from the LHS since it is truthy.

My workaround for this is to use the ternary operator which correctly short-circuits.

massfords avatar Nov 20 '25 17:11 massfords

Here's some code that shows this issue in a js-resolver. Sorry for not having a full deployable endpoint to test against.

var request = () => {

  const RHS_VALUES = [];    
  // A Function for use on the right-hand-side of the expression. 
  // The expectation is that this function should only be called when
  // the left-hand-side is null or undefined.
  const RHS = (msg) => {
      RHS_VALUES.push(msg);
      return msg;
  };
  
  // Assorted values to use for the left-hand-side.
  // Only the null and undefined ones should result in the
  // function on the right-hand-side being executed.
  const LHS_null = null;
  const LHS_undefined = undefined;
  const LHS_boolean_true = true;
  const LHS_boolean_false = false;
  const LHS_string_empty = "";
  const LHS_string = "hello";
  const LHS_object_empty = {};
  const LHS_object = { hello: "world" };
  const LHS_array_empty = [];
  const LHS_array = ["hello"];
  
  const RET_VALUES = [];
  
  // The return values from the expression are kept in the array.
  // These values are correct, but the tests show that the function
  // on the right-hand-side is always executed.
  RET_VALUES.push(LHS_null ?? RHS("LHS_null"));
  RET_VALUES.push(LHS_undefined ?? RHS("LHS_undefined"));
  RET_VALUES.push(LHS_boolean_true ?? RHS("error: LHS_boolean_true"));
  RET_VALUES.push(LHS_boolean_false ?? RHS("error: LHS_boolean_false"));
  RET_VALUES.push(LHS_string_empty ?? RHS("error: LHS_string_empty"));
  RET_VALUES.push(LHS_string ?? RHS("error: LHS_string"));
  RET_VALUES.push(LHS_object_empty ?? RHS("error: LHS_object_empty"));
  RET_VALUES.push(LHS_object ?? RHS("error: LHS_object"));
  RET_VALUES.push(LHS_array_empty ?? RHS("error: LHS_array_empty"));
  RET_VALUES.push(LHS_array ?? RHS("error: LHS_array"));
  
  console.log("expecting RHS_VALUES to only have two entries. One for the null and one for the undefined");
  console.log(JSON.stringify(RHS_VALUES, null, 2));
    
  return {
    payload: null
  };
};
var response = () => {
  return true;
};
export {
  request,
  response
};

Here's the relevant output from the log, formatted to make it easier to read.

expecting RHS_VALUES to only have two entries. One for the null and one for the undefined
[
	"LHS_null",
	"LHS_undefined",
	"error: LHS_boolean_true",
	"error: LHS_boolean_false",
	"error: LHS_string_empty",
	"error: LHS_string",
	"error: LHS_object_empty",
	"error: LHS_object",
	"error: LHS_array_empty",
	"error: LHS_array"
]

massfords avatar Nov 21 '25 18:11 massfords

I have the same problem, hope this gets fixed

stbenjam avatar Dec 17 '25 01:12 stbenjam