Skip to content

Commit

Permalink
feat(isolated-declarations): transform const expression correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
Dunqing committed Jun 20, 2024
1 parent 01da2f7 commit 84f3100
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 26 deletions.
2 changes: 1 addition & 1 deletion crates/oxc_isolated_declarations/src/inferrer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl<'a> IsolatedDeclarations<'a> {
}
Expression::TSAsExpression(expr) => {
if expr.type_annotation.is_const_type_reference() {
Some(self.transform_expression_to_ts_type(&expr.expression))
self.transform_expression_to_ts_type(&expr.expression)
} else {
Some(self.ast.copy(&expr.type_annotation))
}
Expand Down
78 changes: 53 additions & 25 deletions crates/oxc_isolated_declarations/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,11 @@ impl<'a> IsolatedDeclarations<'a> {
}
}

let type_annotation = self.infer_type_from_expression(&object.value);
let type_annotation = if is_const {
self.transform_expression_to_ts_type(&object.value)
} else {
self.infer_type_from_expression(&object.value)
};

if type_annotation.is_none() {
self.error(inferred_type_of_expression(object.value.span()));
Expand Down Expand Up @@ -142,17 +146,19 @@ impl<'a> IsolatedDeclarations<'a> {
is_const: bool,
) -> TSType<'a> {
let element_types =
self.ast.new_vec_from_iter(expr.elements.iter().filter_map(|element| match element {
ArrayExpressionElement::SpreadElement(spread) => {
self.error(arrays_with_spread_elements(spread.span));
None
}
ArrayExpressionElement::Elision(elision) => {
Some(TSTupleElement::from(self.ast.ts_undefined_keyword(elision.span)))
self.ast.new_vec_from_iter(expr.elements.iter().filter_map(|element| {
match element {
ArrayExpressionElement::SpreadElement(spread) => {
self.error(arrays_with_spread_elements(spread.span));
None
}
ArrayExpressionElement::Elision(elision) => {
Some(TSTupleElement::from(self.ast.ts_undefined_keyword(elision.span)))
}
_ => self
.transform_expression_to_ts_type(element.to_expression())
.map(TSTupleElement::from),
}
_ => Some(TSTupleElement::from(
self.transform_expression_to_ts_type(element.to_expression()),
)),
}));

let ts_type = self.ast.ts_tuple_type(SPAN, element_types);
Expand All @@ -164,36 +170,58 @@ impl<'a> IsolatedDeclarations<'a> {
}

// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions
pub fn transform_expression_to_ts_type(&self, expr: &Expression<'a>) -> TSType<'a> {
pub fn transform_expression_to_ts_type(&self, expr: &Expression<'a>) -> Option<TSType<'a>> {
match expr {
Expression::BooleanLiteral(lit) => {
self.ast.ts_literal_type(SPAN, TSLiteral::BooleanLiteral(self.ast.copy(lit)))
Some(self.ast.ts_literal_type(SPAN, TSLiteral::BooleanLiteral(self.ast.copy(lit))))
}
Expression::NumericLiteral(lit) => {
self.ast.ts_literal_type(SPAN, TSLiteral::NumericLiteral(self.ast.copy(lit)))
Some(self.ast.ts_literal_type(SPAN, TSLiteral::NumericLiteral(self.ast.copy(lit))))
}
Expression::BigintLiteral(lit) => {
self.ast.ts_literal_type(SPAN, TSLiteral::BigintLiteral(self.ast.copy(lit)))
Some(self.ast.ts_literal_type(SPAN, TSLiteral::BigintLiteral(self.ast.copy(lit))))
}
Expression::StringLiteral(lit) => {
self.ast.ts_literal_type(SPAN, TSLiteral::StringLiteral(self.ast.copy(lit)))
Some(self.ast.ts_literal_type(SPAN, TSLiteral::StringLiteral(self.ast.copy(lit))))
}
Expression::NullLiteral(lit) => Some(self.ast.ts_null_keyword(lit.span)),
Expression::Identifier(ident) => match ident.name.as_str() {
"undefined" => Some(self.ast.ts_undefined_keyword(ident.span)),
_ => None,
},
Expression::TemplateLiteral(lit) => {
self.ast.ts_literal_type(SPAN, TSLiteral::TemplateLiteral(self.ast.copy(lit)))
}
Expression::UnaryExpression(expr) => {
self.ast.ts_literal_type(SPAN, TSLiteral::UnaryExpression(self.ast.copy(expr)))
if lit.expressions.is_empty() {
lit.quasis.first().map(|item| {
self.ast.ts_literal_type(
SPAN,
TSLiteral::StringLiteral(self.ast.alloc(self.ast.string_literal(
lit.span,
if let Some(cooked) = &item.value.cooked {
cooked
} else {
&item.value.raw
},
))),
)
})
} else {
None
}
}
Expression::UnaryExpression(expr) => Some(
self.ast.ts_literal_type(SPAN, TSLiteral::UnaryExpression(self.ast.copy(expr))),
),
Expression::ArrayExpression(expr) => {
self.transform_array_expression_to_ts_type(expr, true)
Some(self.transform_array_expression_to_ts_type(expr, true))
}
Expression::ObjectExpression(expr) => {
// { readonly a: number }
self.transform_object_expression_to_ts_type(expr, true)
Some(self.transform_object_expression_to_ts_type(expr, true))
}
_ => {
unreachable!()
Expression::FunctionExpression(func) => self.transform_function_to_ts_type(func),
Expression::ArrowFunctionExpression(func) => {
self.transform_arrow_function_to_ts_type(func)
}
_ => None,
}
}
}
16 changes: 16 additions & 0 deletions crates/oxc_isolated_declarations/tests/fixtures/as-const.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const F = {
string: `string`,
templateLiteral: `templateLiteral`,
number: 1.23,
bigint: -1_2_3n,
boolean: true,
null: null,
undefined: undefined,
function(a: string): void {},
arrow: (a: string): void => {},
object: {
a: `a`,
b: `b`
},
array: [`a`, , { b: `\n` }],
} as const
22 changes: 22 additions & 0 deletions crates/oxc_isolated_declarations/tests/snapshots/as-const.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
source: crates/oxc_isolated_declarations/tests/mod.rs
input_file: crates/oxc_isolated_declarations/tests/fixtures/as-const.ts
---
==================== .D.TS ====================

declare const F: {
readonly string: 'string';
readonly templateLiteral: 'templateLiteral';
readonly number: 1.23;
readonly bigint: -1_2_3n;
readonly boolean: true;
readonly null: null;
readonly undefined: undefined;
readonly function: (a: string) => void;
readonly arrow: (a: string) => void;
readonly object: {
readonly a: 'a';
readonly b: 'b';
};
readonly array: readonly ['a', undefined, { readonly b: '\n'}];
};

0 comments on commit 84f3100

Please sign in to comment.