Repository: prettier/prettier (JavaScript)

Issue:
TypeError: Cannot read properties of undefined (reading 'type')
**Prettier 3.2.1**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEACdrJQM41QJwEMoATCAWwDEBXKMAS2lQF5UAKAN0IBtq4BKJKg4R6JFgD5UwADpQMC1PQBm7LrzgA6Qv2lzFB1MtpgAgm32GrMkDYA0lqweN0AQhflOvJQgE8AVticPHzaAPyaMBAAyjD49FAA5mz8-I5eijIwWdokJGwAjHaoNj6+NmmeGZnZMJEQAJLRAPKx8Ukp6dWV3QDcXRgAvumD-VAgdiAQAA4wjDjIoIT4+BAA7gAKywjYyCA8a367kwBGRGAA1nAw0YTkcAAyCXDIyjzYcKfnVzfThAxJZBxPiTD7kehA-AgkBwAAe0zg8XusB4ABVEVBlvQ4LskG9uB9QQlEtw4ABFagQeCvd6fECBWHRYmkilUl542mTACOlPgG1W01x+2wAFooHA4CRJRMQHFCPRuMSAMIUciEPY8bgy7DMuCmGBxegnah8xFPcU0gl0gAWMHI3AA6tb6PBsH8wHBojsXfQOC7fHswNhjiAOHwGqQEDcwPFZqZSLFfKTLYSQNNVh8HURpnt0zjERwXpMEh98DB+YREmqU3S-vhS3sYL4EdgY-RZjL0wkYA6xDBrcgABwABkm+DgPPo44rVfVHKtkxghBOvZI-eQACZJtQPqjl7j8am4OQTpKpSQHsREtRK3BKBB8GqDcSNSaICBBoMgA)
<!-- prettier-ignore -->
```sh
--parser typescript
```

**Input:**
<!-- prettier-ignore -->
```tsx
    const randomFuncion = (value): void => {
        if (value.a) {
            funcA(
                "",
                funcB(
                    dayjs(value.a?.toString())
                  		.add(1, "day")
                  		.toISOString()
                )
            );
        }
    };

```

**Output:**
<!-- prettier-ignore -->
```tsx
TypeError: Cannot read properties of undefined (reading 'type')
    at Jn (https://prettier.io/lib/plugins/estree.js:7:16595)
    at be (https://prettier.io/lib/plugins/estree.js:9:605)
    at be (https://prettier.io/lib/plugins/estree.js:10:209)
    at https://prettier.io/lib/plugins/estree.js:36:14282
    at Array.every (<anonymous>)
    at https://prettier.io/lib/plugins/estree.js:36:14273
    at Array.some (<anonymous>)
    at Oi (https://prettier.io/lib/plugins/estree.js:36:14252)
    at Kr (https://prettier.io/lib/plugins/estree.js:36:14852)
    at da (https://prettier.io/lib/plugins/estree.js:36:58423)
```

**Expected output:**
<!-- prettier-ignore -->
```tsx
const randomFuncion = (value): void => {
  if (value.a) {
    funcA(
      "", 
      funcB(dayjs(value.a?.toString())
        .add(1, "day")
        .toISOString()
      )
    );
  }
};
```

**Why?**
That was not an Error in 3.1.1 here the error in version 3.2.0 which should be fixed in #15912

If I remove the question mark behind the `funcB(dayjs(value.a? ...` the error is gone.
<!-- short explanation of expected output -->


Hints:
I'm getting the same error with `prettier@3.2.0` in my project, here is the minimal reproduction example :

```js
  a(() => {}, c?.d);
```
The workaround is to introduce an additional variable:
```js
const b = c?.d;
a(() => {}, b);
```

Patch:
diff --git a/src/language-js/utils/index.js b/src/language-js/utils/index.js
--- a/src/language-js/utils/index.js
+++ b/src/language-js/utils/index.js
@@ -405,16 +405,6 @@ function isTestCall(node, parent) {
 /** @return {(node: Node) => boolean} */
 const skipChainExpression = (fn) => (node) => {
   if (node?.type === "ChainExpression") {
-    // @ts-expect-error
-    if (!node.object) {
-      // @ts-expect-error
-      node.object = node.expression.object;
-    }
-    // @ts-expect-error
-    if (!node.property) {
-      // @ts-expect-error
-      node.property = node.expression.property;
-    }
     node = node.expression;
   }
 
@@ -429,6 +419,22 @@ const isMemberExpression = skipChainExpression(
   createTypeCheckFunction(["MemberExpression", "OptionalMemberExpression"]),
 );
 
+/**
+ * Retrieves a property from a node, considering any ChainExpression.
+ * If the node is a ChainExpression, the property is obtained from its expression.
+ * Otherwise, the property is obtained directly from the node.
+ *
+ * @param {Node} node - The AST node to be processed.
+ * @param {string} property - The property name to retrieve.
+ * @returns The property value from the node or its expression.
+ */
+function getChainProp(node, property) {
+  if (node.type === "ChainExpression") {
+    return node.expression[property];
+  }
+  return node[property];
+}
+
 /**
  *
  * @param {any} node
@@ -769,7 +775,7 @@ function isSimpleCallArgument(node, depth = 2) {
   if (isCallLikeExpression(node)) {
     if (
       node.type === "ImportExpression" ||
-      isSimpleCallArgument(node.callee, depth)
+      isSimpleCallArgument(getChainProp(node, "callee"), depth)
     ) {
       const args = getCallArguments(node);
       return args.length <= depth && args.every(isChildSimple);
@@ -779,8 +785,8 @@ function isSimpleCallArgument(node, depth = 2) {
 
   if (isMemberExpression(node)) {
     return (
-      isSimpleCallArgument(node.object, depth) &&
-      isSimpleCallArgument(node.property, depth)
+      isSimpleCallArgument(getChainProp(node, "object"), depth) &&
+      isSimpleCallArgument(getChainProp(node, "property"), depth)
     );
   }
 
@@ -1232,6 +1238,7 @@ export {
   createTypeCheckFunction,
   getCallArguments,
   getCallArgumentSelector,
+  getChainProp,
   getComments,
   getFunctionParameters,
   getLeftSide,


Test patch:
diff --git a/tests/format/js/chain-expression/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/chain-expression/__snapshots__/jsfmt.spec.js.snap
--- a/tests/format/js/chain-expression/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/format/js/chain-expression/__snapshots__/jsfmt.spec.js.snap
@@ -97,6 +97,35 @@ test().test2().test2(thing?.something);
 ================================================================================
 `;
 
+exports[`issue-15916.js format 1`] = `
+====================================options=====================================
+parsers: ["babel", "typescript", "flow"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+const randomFuncion = (value) => {
+    if (value.a) {
+        funcA(
+            "",
+            funcB(
+                dayjs(value.a?.toString())
+                      .add(1, "day")
+                      .toISOString()
+            )
+        );
+    }
+};
+
+=====================================output=====================================
+const randomFuncion = (value) => {
+  if (value.a) {
+    funcA("", funcB(dayjs(value.a?.toString()).add(1, "day").toISOString()));
+  }
+};
+
+================================================================================
+`;
+
 exports[`test.js format 1`] = `
 ====================================options=====================================
 parsers: ["babel", "typescript", "flow"]
diff --git a/tests/format/js/chain-expression/issue-15916.js b/tests/format/js/chain-expression/issue-15916.js
new file mode 100644
--- /dev/null
+++ b/tests/format/js/chain-expression/issue-15916.js
@@ -0,0 +1,12 @@
+const randomFuncion = (value) => {
+    if (value.a) {
+        funcA(
+            "",
+            funcB(
+                dayjs(value.a?.toString())
+                      .add(1, "day")
+                      .toISOString()
+            )
+        );
+    }
+};