diff --git a/crates/oxc_ast/src/ast_impl/js.rs b/crates/oxc_ast/src/ast_impl/js.rs index 745008f665e89..9ff086e3fd0ce 100644 --- a/crates/oxc_ast/src/ast_impl/js.rs +++ b/crates/oxc_ast/src/ast_impl/js.rs @@ -104,6 +104,10 @@ impl<'a> Expression<'a> { matches!(self, Self::StringLiteral(_) | Self::TemplateLiteral(_)) } + pub fn is_number_literal(&self) -> bool { + matches!(self, Self::NumericLiteral(_) | Self::BigIntLiteral(_)) + } + pub fn is_specific_string_literal(&self, string: &str) -> bool { match self { Self::StringLiteral(s) => s.value == string, diff --git a/crates/oxc_isolated_declarations/src/inferrer.rs b/crates/oxc_isolated_declarations/src/inferrer.rs index 0bab7117ee695..ea83c09d90390 100644 --- a/crates/oxc_isolated_declarations/src/inferrer.rs +++ b/crates/oxc_isolated_declarations/src/inferrer.rs @@ -1,7 +1,7 @@ use oxc_allocator::Box; use oxc_ast::ast::{ ArrowFunctionExpression, BindingPatternKind, Expression, FormalParameter, Function, Statement, - TSType, TSTypeAnnotation, + TSType, TSTypeAnnotation, UnaryExpression, }; use oxc_span::{GetSpan, SPAN}; @@ -14,6 +14,10 @@ use crate::{ }; impl<'a> IsolatedDeclarations<'a> { + pub fn can_infer_unary_expression(expr: &UnaryExpression<'a>) -> bool { + expr.operator.is_arithmetic() && expr.argument.is_number_literal() + } + pub fn infer_type_from_expression(&self, expr: &Expression<'a>) -> Option> { match expr { Expression::BooleanLiteral(_) => Some(self.ast.ts_boolean_keyword(SPAN)), @@ -66,6 +70,13 @@ impl<'a> IsolatedDeclarations<'a> { self.infer_type_from_expression(&expr.expression) } Expression::TSTypeAssertion(expr) => Some(self.ast.copy(&expr.type_annotation)), + Expression::UnaryExpression(expr) => { + if Self::can_infer_unary_expression(expr) { + self.infer_type_from_expression(&expr.argument) + } else { + None + } + } _ => None, } } @@ -136,12 +147,13 @@ impl<'a> IsolatedDeclarations<'a> { .map(|type_annotation| self.ast.ts_type_annotation(SPAN, type_annotation)) } - pub fn is_need_to_infer_type_from_expression(expr: &Expression) -> bool { + pub fn is_need_to_infer_type_from_expression(expr: &Expression<'a>) -> bool { match expr { Expression::NumericLiteral(_) | Expression::BigIntLiteral(_) | Expression::StringLiteral(_) => false, Expression::TemplateLiteral(lit) => !lit.expressions.is_empty(), + Expression::UnaryExpression(expr) => !Self::can_infer_unary_expression(expr), _ => true, } } diff --git a/crates/oxc_isolated_declarations/src/types.rs b/crates/oxc_isolated_declarations/src/types.rs index ee9cc9860a08d..558a1c6764804 100644 --- a/crates/oxc_isolated_declarations/src/types.rs +++ b/crates/oxc_isolated_declarations/src/types.rs @@ -192,9 +192,16 @@ impl<'a> IsolatedDeclarations<'a> { Expression::TemplateLiteral(lit) => self .transform_template_to_string(lit) .map(|string| self.ast.ts_literal_type(lit.span, TSLiteral::StringLiteral(string))), - Expression::UnaryExpression(expr) => Some( - self.ast.ts_literal_type(SPAN, TSLiteral::UnaryExpression(self.ast.copy(expr))), - ), + Expression::UnaryExpression(expr) => { + if Self::can_infer_unary_expression(expr) { + Some( + self.ast + .ts_literal_type(SPAN, TSLiteral::UnaryExpression(self.ast.copy(expr))), + ) + } else { + None + } + } Expression::ArrayExpression(expr) => { Some(self.transform_array_expression_to_ts_type(expr, true)) } diff --git a/crates/oxc_isolated_declarations/tests/fixtures/infer-expression.ts b/crates/oxc_isolated_declarations/tests/fixtures/infer-expression.ts index 06222db62bcc9..a861197730968 100644 --- a/crates/oxc_isolated_declarations/tests/fixtures/infer-expression.ts +++ b/crates/oxc_isolated_declarations/tests/fixtures/infer-expression.ts @@ -1,5 +1,18 @@ +// Correct + // ParenthesizedExpression const n = (0); const s = (""); const t = (``); -const b = (true); \ No newline at end of file +const b = (true); + +// UnaryExpression +let unaryA = +12; +const unaryB = -1_2n; + +// Incorrect + +// UnaryExpression +const unaryC = +"str" +const unaryD = typeof "str" +const unaryE = {E: -"str"} as const \ No newline at end of file diff --git a/crates/oxc_isolated_declarations/tests/snapshots/infer-expression.snap b/crates/oxc_isolated_declarations/tests/snapshots/infer-expression.snap index 40ca55fc06e7b..1f0c89c099324 100644 --- a/crates/oxc_isolated_declarations/tests/snapshots/infer-expression.snap +++ b/crates/oxc_isolated_declarations/tests/snapshots/infer-expression.snap @@ -8,3 +8,36 @@ declare const n: number; declare const s: string; declare const t: string; declare const b: boolean; +declare let unaryA: number; +declare const unaryB = -1_2n; +declare const unaryC: unknown; +declare const unaryD: unknown; +declare const unaryE: {}; + + +==================== Errors ==================== + + x TS9010: Variable must have an explicit type annotation with + | --isolatedDeclarations. + ,-[16:7] + 15 | // UnaryExpression + 16 | const unaryC = +"str" + : ^^^^^^ + 17 | const unaryD = typeof "str" + `---- + + x TS9010: Variable must have an explicit type annotation with + | --isolatedDeclarations. + ,-[17:7] + 16 | const unaryC = +"str" + 17 | const unaryD = typeof "str" + : ^^^^^^ + 18 | const unaryE = {E: -"str"} as const + `---- + + x TS9013: Expression type can't be inferred with --isolatedDeclarations. + ,-[18:20] + 17 | const unaryD = typeof "str" + 18 | const unaryE = {E: -"str"} as const + : ^^^^^^ + `----