Skip to content

Commit 476d0f1

Browse files
committed
feat(isolated-declarations): transform const expression correctly
1 parent 2a16ce0 commit 476d0f1

File tree

4 files changed

+92
-26
lines changed

4 files changed

+92
-26
lines changed

crates/oxc_isolated_declarations/src/inferrer.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ impl<'a> IsolatedDeclarations<'a> {
4343
}
4444
Expression::TSAsExpression(expr) => {
4545
if expr.type_annotation.is_const_type_reference() {
46-
Some(self.transform_expression_to_ts_type(&expr.expression))
46+
self.transform_expression_to_ts_type(&expr.expression)
4747
} else {
4848
Some(self.ast.copy(&expr.type_annotation))
4949
}

crates/oxc_isolated_declarations/src/types.rs

+53-25
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,11 @@ impl<'a> IsolatedDeclarations<'a> {
108108
}
109109
}
110110

111-
let type_annotation = self.infer_type_from_expression(&object.value);
111+
let type_annotation = if is_const {
112+
self.transform_expression_to_ts_type(&object.value)
113+
} else {
114+
self.infer_type_from_expression(&object.value)
115+
};
112116

113117
if type_annotation.is_none() {
114118
self.error(inferred_type_of_expression(object.value.span()));
@@ -142,17 +146,19 @@ impl<'a> IsolatedDeclarations<'a> {
142146
is_const: bool,
143147
) -> TSType<'a> {
144148
let element_types =
145-
self.ast.new_vec_from_iter(expr.elements.iter().filter_map(|element| match element {
146-
ArrayExpressionElement::SpreadElement(spread) => {
147-
self.error(arrays_with_spread_elements(spread.span));
148-
None
149-
}
150-
ArrayExpressionElement::Elision(elision) => {
151-
Some(TSTupleElement::from(self.ast.ts_undefined_keyword(elision.span)))
149+
self.ast.new_vec_from_iter(expr.elements.iter().filter_map(|element| {
150+
match element {
151+
ArrayExpressionElement::SpreadElement(spread) => {
152+
self.error(arrays_with_spread_elements(spread.span));
153+
None
154+
}
155+
ArrayExpressionElement::Elision(elision) => {
156+
Some(TSTupleElement::from(self.ast.ts_undefined_keyword(elision.span)))
157+
}
158+
_ => self
159+
.transform_expression_to_ts_type(element.to_expression())
160+
.map(TSTupleElement::from),
152161
}
153-
_ => Some(TSTupleElement::from(
154-
self.transform_expression_to_ts_type(element.to_expression()),
155-
)),
156162
}));
157163

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

166172
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions
167-
pub fn transform_expression_to_ts_type(&self, expr: &Expression<'a>) -> TSType<'a> {
173+
pub fn transform_expression_to_ts_type(&self, expr: &Expression<'a>) -> Option<TSType<'a>> {
168174
match expr {
169175
Expression::BooleanLiteral(lit) => {
170-
self.ast.ts_literal_type(SPAN, TSLiteral::BooleanLiteral(self.ast.copy(lit)))
176+
Some(self.ast.ts_literal_type(SPAN, TSLiteral::BooleanLiteral(self.ast.copy(lit))))
171177
}
172178
Expression::NumericLiteral(lit) => {
173-
self.ast.ts_literal_type(SPAN, TSLiteral::NumericLiteral(self.ast.copy(lit)))
179+
Some(self.ast.ts_literal_type(SPAN, TSLiteral::NumericLiteral(self.ast.copy(lit))))
174180
}
175181
Expression::BigintLiteral(lit) => {
176-
self.ast.ts_literal_type(SPAN, TSLiteral::BigintLiteral(self.ast.copy(lit)))
182+
Some(self.ast.ts_literal_type(SPAN, TSLiteral::BigintLiteral(self.ast.copy(lit))))
177183
}
178184
Expression::StringLiteral(lit) => {
179-
self.ast.ts_literal_type(SPAN, TSLiteral::StringLiteral(self.ast.copy(lit)))
185+
Some(self.ast.ts_literal_type(SPAN, TSLiteral::StringLiteral(self.ast.copy(lit))))
180186
}
187+
Expression::NullLiteral(lit) => Some(self.ast.ts_null_keyword(lit.span)),
188+
Expression::Identifier(ident) => match ident.name.as_str() {
189+
"undefined" => Some(self.ast.ts_undefined_keyword(ident.span)),
190+
_ => None,
191+
},
181192
Expression::TemplateLiteral(lit) => {
182-
self.ast.ts_literal_type(SPAN, TSLiteral::TemplateLiteral(self.ast.copy(lit)))
183-
}
184-
Expression::UnaryExpression(expr) => {
185-
self.ast.ts_literal_type(SPAN, TSLiteral::UnaryExpression(self.ast.copy(expr)))
193+
if lit.expressions.is_empty() {
194+
lit.quasis.first().map(|item| {
195+
self.ast.ts_literal_type(
196+
SPAN,
197+
TSLiteral::StringLiteral(self.ast.alloc(self.ast.string_literal(
198+
lit.span,
199+
if let Some(cooked) = &item.value.cooked {
200+
cooked
201+
} else {
202+
&item.value.raw
203+
},
204+
))),
205+
)
206+
})
207+
} else {
208+
None
209+
}
186210
}
211+
Expression::UnaryExpression(expr) => Some(
212+
self.ast.ts_literal_type(SPAN, TSLiteral::UnaryExpression(self.ast.copy(expr))),
213+
),
187214
Expression::ArrayExpression(expr) => {
188-
self.transform_array_expression_to_ts_type(expr, true)
215+
Some(self.transform_array_expression_to_ts_type(expr, true))
189216
}
190217
Expression::ObjectExpression(expr) => {
191-
// { readonly a: number }
192-
self.transform_object_expression_to_ts_type(expr, true)
218+
Some(self.transform_object_expression_to_ts_type(expr, true))
193219
}
194-
_ => {
195-
unreachable!()
220+
Expression::FunctionExpression(func) => self.transform_function_to_ts_type(func),
221+
Expression::ArrowFunctionExpression(func) => {
222+
self.transform_arrow_function_to_ts_type(func)
196223
}
224+
_ => None,
197225
}
198226
}
199227
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const F = {
2+
string: `string`,
3+
templateLiteral: `templateLiteral`,
4+
number: 1.23,
5+
bigint: -1_2_3n,
6+
boolean: true,
7+
null: null,
8+
undefined: undefined,
9+
function(a: string): void {},
10+
arrow: (a: string): void => {},
11+
object: {
12+
a: `a`,
13+
b: `b`
14+
},
15+
array: [`a`, , { b: `\n` }],
16+
} as const
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
source: crates/oxc_isolated_declarations/tests/mod.rs
3+
input_file: crates/oxc_isolated_declarations/tests/fixtures/as-const.ts
4+
---
5+
==================== .D.TS ====================
6+
7+
declare const F: {
8+
readonly string: 'string';
9+
readonly templateLiteral: 'templateLiteral';
10+
readonly number: 1.23;
11+
readonly bigint: -1_2_3n;
12+
readonly boolean: true;
13+
readonly null: null;
14+
readonly undefined: undefined;
15+
readonly function: (a: string) => void;
16+
readonly arrow: (a: string) => void;
17+
readonly object: {
18+
readonly a: 'a';
19+
readonly b: 'b';
20+
};
21+
readonly array: readonly ['a', undefined, { readonly b: '\n'}];
22+
};

0 commit comments

Comments
 (0)