Repository: swc-project/swc (rust)

Issue:
Unexpected behaviour of optional chaining lead to reading property of 'undefined'
### Describe the bug

Using optional chaining follow by non-null assertion operator can potentially cause error: `Cannot read property 'xxx' of undefined`

### Input code

```typescript
const a = undefined
a?.b!.c
```


### Config

_No response_

### Playground link

https://play.swc.rs/?version=1.3.63&code=H4sIAAAAAAAAA0vOzysuUUhUsFUozUtJTcvMS03hSrTXS1LUSwYAirn80xsAAAA%3D&config=H4sIAAAAAAAAA2WPwQqDQAxE7%2F0KydmCWHrpP%2FTa%2B7LGsuJuJImgiP%2FeaFUsvSWTN7Oz0yXLoBEPj2yy0RZll6QmjodkYoWe2CnxC1kCJbtBWZTltbjBysz51905FuSzVcakblgMOnYonkOnkP8FixHKPR4XlWGTfh5Qx2%2FUJQ7lvuVASyRoWu1aWRNWGiJVfYunr1mBxekpRkqNwEEGee7sXgJiSKEe9w7zB0wx7NgqAQAA

### Expected behavior

output of tsc is correct and run with no error

```js
"use strict";
const a = undefined;
a === null || a === void 0 ? void 0 : a.b.c;
```

### Actual behavior

output of swc will cause error: Cannot read property 'c' of undefined

```js
"use strict";var _a;var a=undefined;((_a=a)===null||_a===void 0?void 0:_a.b).c;
```

### Version

1.3.63

### Additional context

_No response_

Pull request:
fix(es/typescript): Fix handling of optional chaining
**Description:**


**Related issue:**

 - Closes #7659.

Patch:
diff --git a/crates/swc_ecma_transforms_typescript/src/strip.rs b/crates/swc_ecma_transforms_typescript/src/strip.rs
index 3118a019f3..6c7419892f 100644
--- a/crates/swc_ecma_transforms_typescript/src/strip.rs
+++ b/crates/swc_ecma_transforms_typescript/src/strip.rs
@@ -448,6 +448,7 @@ where
             | Expr::TsSatisfies(TsSatisfiesExpr { expr, .. }) => {
                 expr.visit_mut_with(self);
                 let expr = *expr.take();
+
                 *n = expr;
             }
 
@@ -2042,7 +2043,7 @@ where
     }
 
     fn visit_mut_expr(&mut self, n: &mut Expr) {
-        let mut stack = vec![n];
+        let mut stack = vec![&mut *n];
         loop {
             let mut new_stack = vec![];
             for expr in stack {
@@ -2052,6 +2053,37 @@ where
             }
 
             if new_stack.is_empty() {
+                // https://github.com/swc-project/swc/issues/7659
+                // a?.b!.c
+                // should be identical to
+                // a?.b.c
+                // and it's optional chaining expression with optional = false
+                match n {
+                    Expr::Member(me) if me.obj.is_opt_chain() => {
+                        *n = Expr::OptChain(OptChainExpr {
+                            span: me.span,
+                            optional: false,
+                            base: Box::new(OptChainBase::Member(me.take())),
+                        });
+                    }
+
+                    Expr::Call(ce)
+                        if ce.callee.is_expr() && ce.callee.as_expr().unwrap().is_opt_chain() =>
+                    {
+                        *n = Expr::OptChain(OptChainExpr {
+                            span: ce.span,
+                            optional: false,
+                            base: Box::new(OptChainBase::Call(OptCall {
+                                span: ce.span,
+                                callee: ce.callee.take().expect_expr(),
+                                args: ce.args.take(),
+                                type_args: ce.type_args.take(),
+                            })),
+                        });
+                    }
+
+                    _ => {}
+                }
                 return;
             }
 


Test patch:
diff --git a/crates/swc/tests/fixture/issues-7xxx/7659/1/input/.swcrc b/crates/swc/tests/fixture/issues-7xxx/7659/1/input/.swcrc
new file mode 100644
index 0000000000..0f581b7086
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-7xxx/7659/1/input/.swcrc
@@ -0,0 +1,19 @@
+{
+    "jsc": {
+        "transform": {
+            "decoratorVersion": "2022-03"
+        },
+        "parser": {
+            "syntax": "typescript",
+            "decorators": true,
+            "tsx": true
+        },
+        "target": "es5",
+        "loose": false,
+    },
+    "module": {
+        "type": "commonjs"
+    },
+    "isModule": true,
+    "minify": true
+}
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-7xxx/7659/1/input/1.ts b/crates/swc/tests/fixture/issues-7xxx/7659/1/input/1.ts
new file mode 100644
index 0000000000..9d89b144d5
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-7xxx/7659/1/input/1.ts
@@ -0,0 +1,2 @@
+const a = undefined
+a?.b!.c
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-7xxx/7659/1/output/1.ts b/crates/swc/tests/fixture/issues-7xxx/7659/1/output/1.ts
new file mode 100644
index 0000000000..81f509133d
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-7xxx/7659/1/output/1.ts
@@ -0,0 +1,1 @@
+"use strict";var _a;var a=undefined;(_a=a)===null||_a===void 0?void 0:_a.b.c;
diff --git a/crates/swc/tests/fixture/issues-7xxx/7659/2/input/.swcrc b/crates/swc/tests/fixture/issues-7xxx/7659/2/input/.swcrc
new file mode 100644
index 0000000000..0f581b7086
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-7xxx/7659/2/input/.swcrc
@@ -0,0 +1,19 @@
+{
+    "jsc": {
+        "transform": {
+            "decoratorVersion": "2022-03"
+        },
+        "parser": {
+            "syntax": "typescript",
+            "decorators": true,
+            "tsx": true
+        },
+        "target": "es5",
+        "loose": false,
+    },
+    "module": {
+        "type": "commonjs"
+    },
+    "isModule": true,
+    "minify": true
+}
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-7xxx/7659/2/input/1.ts b/crates/swc/tests/fixture/issues-7xxx/7659/2/input/1.ts
new file mode 100644
index 0000000000..083938c24e
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-7xxx/7659/2/input/1.ts
@@ -0,0 +1,2 @@
+const a = undefined
+a?.b.c
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-7xxx/7659/2/output/1.ts b/crates/swc/tests/fixture/issues-7xxx/7659/2/output/1.ts
new file mode 100644
index 0000000000..81f509133d
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-7xxx/7659/2/output/1.ts
@@ -0,0 +1,1 @@
+"use strict";var _a;var a=undefined;(_a=a)===null||_a===void 0?void 0:_a.b.c;
diff --git a/crates/swc/tests/tsc-references/callChain.1.normal.js b/crates/swc/tests/tsc-references/callChain.1.normal.js
index 269ea53de2..b2af47b385 100644
--- a/crates/swc/tests/tsc-references/callChain.1.normal.js
+++ b/crates/swc/tests/tsc-references/callChain.1.normal.js
@@ -82,5 +82,5 @@ _o212, _o213;
 ]).c;
 var v = (_o4 = o4) === null || _o4 === void 0 ? void 0 : _o4(incr);
 (_o5 = o5()) === null || _o5 === void 0 ? void 0 : _o5();
-((_o212 = o2) === null || _o212 === void 0 ? void 0 : _o212.b()).toString;
-((_o213 = o2) === null || _o213 === void 0 ? void 0 : _o213.b()).toString;
+(_o212 = o2) === null || _o212 === void 0 ? void 0 : _o212.b().toString;
+(_o213 = o2) === null || _o213 === void 0 ? void 0 : _o213.b().toString;
diff --git a/crates/swc/tests/tsc-references/callChain.2.minified.js b/crates/swc/tests/tsc-references/callChain.2.minified.js
index 301cfade1a..0ae5f8122c 100644
--- a/crates/swc/tests/tsc-references/callChain.2.minified.js
+++ b/crates/swc/tests/tsc-references/callChain.2.minified.js
@@ -54,4 +54,4 @@ null === (_o1 = o1) || void 0 === _o1 || _o1(), null === (_o11 = o1) || void 0 =
     2,
     3,
     4
-]).c, null === (_o4 = o4) || void 0 === _o4 || _o4(incr), null === (_o5 = o5()) || void 0 === _o5 || _o5(), (null === (_o212 = o2) || void 0 === _o212 ? void 0 : _o212.b()).toString, (null === (_o213 = o2) || void 0 === _o213 ? void 0 : _o213.b()).toString;
+]).c, null === (_o4 = o4) || void 0 === _o4 || _o4(incr), null === (_o5 = o5()) || void 0 === _o5 || _o5(), null === (_o212 = o2) || void 0 === _o212 || _o212.b().toString, null === (_o213 = o2) || void 0 === _o213 || _o213.b().toString;
diff --git a/crates/swc/tests/tsc-references/elementAccessChain.1.normal.js b/crates/swc/tests/tsc-references/elementAccessChain.1.normal.js
index 02fdbce0b7..94cf3acee3 100644
--- a/crates/swc/tests/tsc-references/elementAccessChain.1.normal.js
+++ b/crates/swc/tests/tsc-references/elementAccessChain.1.normal.js
@@ -13,7 +13,7 @@ _o22, _o23, _o24, _o25;
 (_o5_b2 = (_o52 = o5)["b"]) === null || _o5_b2 === void 0 ? void 0 : (_o5_b_c_d2 = _o5_b2.call(_o52)["c"].d) === null || _o5_b_c_d2 === void 0 ? void 0 : _o5_b_c_d2.e;
 (_o5_b3 = (_o53 = o5)["b"]) === null || _o5_b3 === void 0 ? void 0 : (_o5_b_c_d3 = _o5_b3.call(_o53)["c"].d) === null || _o5_b_c_d3 === void 0 ? void 0 : _o5_b_c_d3["e"];
 (_o6 = o6()) === null || _o6 === void 0 ? void 0 : _o6["x"];
-((_o22 = o2) === null || _o22 === void 0 ? void 0 : _o22["b"]).c;
-((_o23 = o2) === null || _o23 === void 0 ? void 0 : _o23["b"])["c"];
-((_o24 = o2) === null || _o24 === void 0 ? void 0 : _o24["b"]).c;
-((_o25 = o2) === null || _o25 === void 0 ? void 0 : _o25["b"])["c"];
+(_o22 = o2) === null || _o22 === void 0 ? void 0 : _o22["b"].c;
+(_o23 = o2) === null || _o23 === void 0 ? void 0 : _o23["b"]["c"];
+(_o24 = o2) === null || _o24 === void 0 ? void 0 : _o24["b"].c;
+(_o25 = o2) === null || _o25 === void 0 ? void 0 : _o25["b"]["c"];
diff --git a/crates/swc/tests/tsc-references/elementAccessChain.2.minified.js b/crates/swc/tests/tsc-references/elementAccessChain.2.minified.js
index 75f5614c8c..d666f56e12 100644
--- a/crates/swc/tests/tsc-references/elementAccessChain.2.minified.js
+++ b/crates/swc/tests/tsc-references/elementAccessChain.2.minified.js
@@ -1,3 +1,3 @@
 //// [elementAccessChain.ts]
 var _o1, _o2, _o21, _o3_b, _o3_b1, _o4_b_c_d, _o4_b, _o4_b_c_d1, _o4_b1, _o5_b_c_d, _o5_b, _o5, _o5_b_c_d1, _o5_b1, _o51, _o5_b_c_d2, _o5_b2, _o52, _o5_b_c_d3, _o5_b3, _o53, _o6, _o22, _o23, _o24, _o25;
-null === (_o1 = o1) || void 0 === _o1 || _o1.b, null === (_o2 = o2) || void 0 === _o2 || _o2.b.c, null === (_o21 = o2) || void 0 === _o21 || _o21.b.c, null === (_o3_b = o3.b) || void 0 === _o3_b || _o3_b.c, null === (_o3_b1 = o3.b) || void 0 === _o3_b1 || _o3_b1.c, null === (_o4_b = o4.b) || void 0 === _o4_b || null === (_o4_b_c_d = _o4_b.c.d) || void 0 === _o4_b_c_d || _o4_b_c_d.e, null === (_o4_b1 = o4.b) || void 0 === _o4_b1 || null === (_o4_b_c_d1 = _o4_b1.c.d) || void 0 === _o4_b_c_d1 || _o4_b_c_d1.e, null === (_o5_b = (_o5 = o5).b) || void 0 === _o5_b || null === (_o5_b_c_d = _o5_b.call(_o5).c.d) || void 0 === _o5_b_c_d || _o5_b_c_d.e, null === (_o5_b1 = (_o51 = o5).b) || void 0 === _o5_b1 || null === (_o5_b_c_d1 = _o5_b1.call(_o51).c.d) || void 0 === _o5_b_c_d1 || _o5_b_c_d1.e, null === (_o5_b2 = (_o52 = o5).b) || void 0 === _o5_b2 || null === (_o5_b_c_d2 = _o5_b2.call(_o52).c.d) || void 0 === _o5_b_c_d2 || _o5_b_c_d2.e, null === (_o5_b3 = (_o53 = o5).b) || void 0 === _o5_b3 || null === (_o5_b_c_d3 = _o5_b3.call(_o53).c.d) || void 0 === _o5_b_c_d3 || _o5_b_c_d3.e, null === (_o6 = o6()) || void 0 === _o6 || _o6.x, (null === (_o22 = o2) || void 0 === _o22 ? void 0 : _o22.b).c, (null === (_o23 = o2) || void 0 === _o23 ? void 0 : _o23.b).c, (null === (_o24 = o2) || void 0 === _o24 ? void 0 : _o24.b).c, (null === (_o25 = o2) || void 0 === _o25 ? void 0 : _o25.b).c;
+null === (_o1 = o1) || void 0 === _o1 || _o1.b, null === (_o2 = o2) || void 0 === _o2 || _o2.b.c, null === (_o21 = o2) || void 0 === _o21 || _o21.b.c, null === (_o3_b = o3.b) || void 0 === _o3_b || _o3_b.c, null === (_o3_b1 = o3.b) || void 0 === _o3_b1 || _o3_b1.c, null === (_o4_b = o4.b) || void 0 === _o4_b || null === (_o4_b_c_d = _o4_b.c.d) || void 0 === _o4_b_c_d || _o4_b_c_d.e, null === (_o4_b1 = o4.b) || void 0 === _o4_b1 || null === (_o4_b_c_d1 = _o4_b1.c.d) || void 0 === _o4_b_c_d1 || _o4_b_c_d1.e, null === (_o5_b = (_o5 = o5).b) || void 0 === _o5_b || null === (_o5_b_c_d = _o5_b.call(_o5).c.d) || void 0 === _o5_b_c_d || _o5_b_c_d.e, null === (_o5_b1 = (_o51 = o5).b) || void 0 === _o5_b1 || null === (_o5_b_c_d1 = _o5_b1.call(_o51).c.d) || void 0 === _o5_b_c_d1 || _o5_b_c_d1.e, null === (_o5_b2 = (_o52 = o5).b) || void 0 === _o5_b2 || null === (_o5_b_c_d2 = _o5_b2.call(_o52).c.d) || void 0 === _o5_b_c_d2 || _o5_b_c_d2.e, null === (_o5_b3 = (_o53 = o5).b) || void 0 === _o5_b3 || null === (_o5_b_c_d3 = _o5_b3.call(_o53).c.d) || void 0 === _o5_b_c_d3 || _o5_b_c_d3.e, null === (_o6 = o6()) || void 0 === _o6 || _o6.x, null === (_o22 = o2) || void 0 === _o22 || _o22.b.c, null === (_o23 = o2) || void 0 === _o23 || _o23.b.c, null === (_o24 = o2) || void 0 === _o24 || _o24.b.c, null === (_o25 = o2) || void 0 === _o25 || _o25.b.c;
diff --git a/crates/swc/tests/tsc-references/propertyAccessChain.1.normal.js b/crates/swc/tests/tsc-references/propertyAccessChain.1.normal.js
index 22ad690aeb..5a36d88f4a 100644
--- a/crates/swc/tests/tsc-references/propertyAccessChain.1.normal.js
+++ b/crates/swc/tests/tsc-references/propertyAccessChain.1.normal.js
@@ -9,5 +9,5 @@ _o21, _o22;
 (_o5_b = (_o5 = o5).b) === null || _o5_b === void 0 ? void 0 : (_o5_b_c_d = _o5_b.call(_o5).c.d) === null || _o5_b_c_d === void 0 ? void 0 : _o5_b_c_d.e;
 (_o6 = o6()) === null || _o6 === void 0 ? void 0 : _o6.x;
 ((_o11 = o1) === null || _o11 === void 0 ? void 0 : _o11.b) ? 1 : 0;
-((_o21 = o2) === null || _o21 === void 0 ? void 0 : _o21.b).c;
-((_o22 = o2) === null || _o22 === void 0 ? void 0 : _o22.b).c;
+(_o21 = o2) === null || _o21 === void 0 ? void 0 : _o21.b.c;
+(_o22 = o2) === null || _o22 === void 0 ? void 0 : _o22.b.c;
diff --git a/crates/swc/tests/tsc-references/propertyAccessChain.2.minified.js b/crates/swc/tests/tsc-references/propertyAccessChain.2.minified.js
index 832cd376a8..cbb8b22560 100644
--- a/crates/swc/tests/tsc-references/propertyAccessChain.2.minified.js
+++ b/crates/swc/tests/tsc-references/propertyAccessChain.2.minified.js
@@ -1,3 +1,3 @@
 //// [propertyAccessChain.ts]
 var _o1, _o2, _o3_b, _o4_b_c_d, _o4_b, _o5_b_c_d, _o5_b, _o5, _o6, _o11, _o21, _o22;
-null === (_o1 = o1) || void 0 === _o1 || _o1.b, null === (_o2 = o2) || void 0 === _o2 || _o2.b.c, null === (_o3_b = o3.b) || void 0 === _o3_b || _o3_b.c, null === (_o4_b = o4.b) || void 0 === _o4_b || null === (_o4_b_c_d = _o4_b.c.d) || void 0 === _o4_b_c_d || _o4_b_c_d.e, null === (_o5_b = (_o5 = o5).b) || void 0 === _o5_b || null === (_o5_b_c_d = _o5_b.call(_o5).c.d) || void 0 === _o5_b_c_d || _o5_b_c_d.e, null === (_o6 = o6()) || void 0 === _o6 || _o6.x, null === (_o11 = o1) || void 0 === _o11 || _o11.b, (null === (_o21 = o2) || void 0 === _o21 ? void 0 : _o21.b).c, (null === (_o22 = o2) || void 0 === _o22 ? void 0 : _o22.b).c;
+null === (_o1 = o1) || void 0 === _o1 || _o1.b, null === (_o2 = o2) || void 0 === _o2 || _o2.b.c, null === (_o3_b = o3.b) || void 0 === _o3_b || _o3_b.c, null === (_o4_b = o4.b) || void 0 === _o4_b || null === (_o4_b_c_d = _o4_b.c.d) || void 0 === _o4_b_c_d || _o4_b_c_d.e, null === (_o5_b = (_o5 = o5).b) || void 0 === _o5_b || null === (_o5_b_c_d = _o5_b.call(_o5).c.d) || void 0 === _o5_b_c_d || _o5_b_c_d.e, null === (_o6 = o6()) || void 0 === _o6 || _o6.x, null === (_o11 = o1) || void 0 === _o11 || _o11.b, null === (_o21 = o2) || void 0 === _o21 || _o21.b.c, null === (_o22 = o2) || void 0 === _o22 || _o22.b.c;