Skip to content

Commit ad14403

Browse files
committed
feat(minifier): compress typeof a.b === 'undefined' to a.b === void 0 (#8751)
The special behavior of `typeof` is only for the identifier, so it should be safe to compress `typeof a.b === 'undefined'` (and other expressions) to `a.b === void 0`.
1 parent f7f2d2f commit ad14403

File tree

2 files changed

+23
-25
lines changed

2 files changed

+23
-25
lines changed

crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs

+21-23
Original file line numberDiff line numberDiff line change
@@ -156,10 +156,12 @@ impl<'a> PeepholeOptimizations {
156156

157157
/// Compress `typeof foo == "undefined"`
158158
///
159-
/// - `typeof foo == "undefined"` (if foo is resolved) -> `foo === undefined`
160-
/// - `typeof foo != "undefined"` (if foo is resolved) -> `foo !== undefined`
161-
/// - `typeof foo == "undefined"` -> `typeof foo > "u"`
162-
/// - `typeof foo != "undefined"` -> `typeof foo < "u"`
159+
/// - `typeof foo == "undefined"` (if foo is not resolved) -> `typeof foo > "u"`
160+
/// - `typeof foo != "undefined"` (if foo is not resolved) -> `typeof foo < "u"`
161+
/// - `typeof foo == "undefined"` -> `foo === undefined`
162+
/// - `typeof foo != "undefined"` -> `foo !== undefined`
163+
/// - `typeof foo.bar == "undefined"` -> `foo.bar === undefined` (for any expression e.g.`typeof (foo + "")`)
164+
/// - `typeof foo.bar != "undefined"` -> `foo.bar !== undefined` (for any expression e.g.`typeof (foo + "")`)
163165
///
164166
/// Enabled by `compress.typeofs`
165167
fn try_compress_typeof_undefined(
@@ -183,24 +185,19 @@ impl<'a> PeepholeOptimizations {
183185
_ => return None,
184186
};
185187
if let Expression::Identifier(ident) = &unary_expr.argument {
186-
if !ctx.is_global_reference(ident) {
187-
let Expression::UnaryExpression(unary_expr) =
188-
ctx.ast.move_expression(&mut expr.left)
189-
else {
190-
unreachable!()
191-
};
192-
let right = ctx.ast.void_0(expr.right.span());
193-
return Some(ctx.ast.expression_binary(
194-
expr.span,
195-
unary_expr.unbox().argument,
196-
new_eq_op,
197-
right,
198-
));
188+
if ctx.is_global_reference(ident) {
189+
let left = ctx.ast.move_expression(&mut expr.left);
190+
let right = ctx.ast.expression_string_literal(expr.right.span(), "u", None);
191+
return Some(ctx.ast.expression_binary(expr.span, left, new_comp_op, right));
199192
}
193+
}
194+
195+
let Expression::UnaryExpression(unary_expr) = ctx.ast.move_expression(&mut expr.left)
196+
else {
197+
unreachable!()
200198
};
201-
let left = ctx.ast.move_expression(&mut expr.left);
202-
let right = ctx.ast.expression_string_literal(expr.right.span(), "u", None);
203-
Some(ctx.ast.expression_binary(expr.span, left, new_comp_op, right))
199+
let right = ctx.ast.void_0(expr.right.span());
200+
Some(ctx.ast.expression_binary(expr.span, unary_expr.unbox().argument, new_eq_op, right))
204201
}
205202

206203
/// `a || (b || c);` -> `(a || b) || c;`
@@ -1498,6 +1495,10 @@ mod test {
14981495
test("typeof x !== 'undefined'; var x", "x !== void 0; var x");
14991496
// input and output both errors with same TDZ error
15001497
test("typeof x !== 'undefined'; let x", "x !== void 0; let x");
1498+
1499+
test("typeof x.y === 'undefined'", "x.y === void 0");
1500+
test("typeof x.y !== 'undefined'", "x.y !== void 0");
1501+
test("typeof (x + '') === 'undefined'", "x + '' === void 0");
15011502
}
15021503

15031504
/// Port from <https://github.com/evanw/esbuild/blob/v0.24.2/internal/js_parser/js_parser_test.go#L4658>
@@ -1512,9 +1513,6 @@ mod test {
15121513
test("typeof x == 'undefined'", "typeof x > 'u'");
15131514
test("'undefined' === typeof x", "typeof x > 'u'");
15141515
test("'undefined' == typeof x", "typeof x > 'u'");
1515-
1516-
test("typeof x.y === 'undefined'", "typeof x.y > 'u'");
1517-
test("typeof x.y !== 'undefined'", "typeof x.y < 'u'");
15181516
}
15191517

15201518
#[test]

tasks/minsize/minsize.snap

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Original | minified | minified | gzip | gzip | Fixture
55

66
173.90 kB | 59.68 kB | 59.82 kB | 19.25 kB | 19.33 kB | moment.js
77

8-
287.63 kB | 89.54 kB | 90.07 kB | 31.07 kB | 31.95 kB | jquery.js
8+
287.63 kB | 89.52 kB | 90.07 kB | 31.07 kB | 31.95 kB | jquery.js
99

1010
342.15 kB | 117.69 kB | 118.14 kB | 43.66 kB | 44.37 kB | vue.js
1111

@@ -21,7 +21,7 @@ Original | minified | minified | gzip | gzip | Fixture
2121

2222
3.20 MB | 1.01 MB | 1.01 MB | 325.18 kB | 331.56 kB | echarts.js
2323

24-
6.69 MB | 2.30 MB | 2.31 MB | 469.99 kB | 488.28 kB | antd.js
24+
6.69 MB | 2.30 MB | 2.31 MB | 469.97 kB | 488.28 kB | antd.js
2525

2626
10.95 MB | 3.37 MB | 3.49 MB | 866.63 kB | 915.50 kB | typescript.js
2727

0 commit comments

Comments
 (0)