Skip to content

Commit ebbb949

Browse files
committed
feat(isolated-declarations): improve inferring the type of accessor
1 parent e05e13e commit ebbb949

File tree

1 file changed

+144
-82
lines changed
  • crates/oxc_isolated_declarations/src

1 file changed

+144
-82
lines changed

crates/oxc_isolated_declarations/src/class.rs

+144-82
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ impl<'a> IsolatedDeclarations<'a> {
3232
}
3333

3434
pub fn report_property_key(&self, key: &PropertyKey<'a>, computed: bool) -> bool {
35-
if computed && self.is_literal_key(key) {
36-
computed_property_name(key.span());
35+
if computed && !self.is_literal_key(key) {
36+
self.error(computed_property_name(key.span()));
3737
true
3838
} else {
3939
false
@@ -51,7 +51,7 @@ impl<'a> IsolatedDeclarations<'a> {
5151
}
5252
}
5353

54-
pub fn transform_class_property_definition(
54+
fn transform_class_property_definition(
5555
&self,
5656
property: &PropertyDefinition<'a>,
5757
) -> ClassElement<'a> {
@@ -95,31 +95,14 @@ impl<'a> IsolatedDeclarations<'a> {
9595
)
9696
}
9797

98-
pub fn transform_class_method_definition(
98+
fn transform_class_method_definition(
9999
&self,
100100
definition: &MethodDefinition<'a>,
101101
params: Box<'a, FormalParameters<'a>>,
102102
return_type: Option<Box<'a, TSTypeAnnotation<'a>>>,
103103
) -> ClassElement<'a> {
104104
let function = &definition.value;
105105

106-
if definition.accessibility.is_some_and(|a| a.is_private()) {
107-
let r#type = match definition.r#type {
108-
MethodDefinitionType::MethodDefinition => {
109-
PropertyDefinitionType::PropertyDefinition
110-
}
111-
MethodDefinitionType::TSAbstractMethodDefinition => {
112-
PropertyDefinitionType::TSAbstractPropertyDefinition
113-
}
114-
};
115-
return self.create_class_property(
116-
r#type,
117-
self.ast.copy(&definition.key),
118-
definition.r#override,
119-
self.transform_accessibility(definition.accessibility),
120-
);
121-
}
122-
123106
let value = self.ast.function(
124107
FunctionType::TSEmptyBodyFunctionExpression,
125108
function.span,
@@ -149,7 +132,7 @@ impl<'a> IsolatedDeclarations<'a> {
149132
)
150133
}
151134

152-
pub fn create_class_property(
135+
fn create_class_property(
153136
&self,
154137
r#type: PropertyDefinitionType,
155138
key: PropertyKey<'a>,
@@ -174,7 +157,7 @@ impl<'a> IsolatedDeclarations<'a> {
174157
)
175158
}
176159

177-
pub fn transform_formal_parameter_to_class_property(
160+
fn transform_formal_parameter_to_class_property(
178161
&self,
179162
param: &FormalParameter<'a>,
180163
type_annotation: Option<Box<'a, TSTypeAnnotation<'a>>>,
@@ -202,6 +185,69 @@ impl<'a> IsolatedDeclarations<'a> {
202185
))
203186
}
204187

188+
fn transform_private_modifier_method(&self, method: &MethodDefinition<'a>) -> ClassElement<'a> {
189+
match method.kind {
190+
MethodDefinitionKind::Method => {
191+
let r#type = match method.r#type {
192+
MethodDefinitionType::MethodDefinition => {
193+
PropertyDefinitionType::PropertyDefinition
194+
}
195+
MethodDefinitionType::TSAbstractMethodDefinition => {
196+
PropertyDefinitionType::TSAbstractPropertyDefinition
197+
}
198+
};
199+
self.create_class_property(
200+
r#type,
201+
self.ast.copy(&method.key),
202+
method.r#override,
203+
self.transform_accessibility(method.accessibility),
204+
)
205+
}
206+
MethodDefinitionKind::Get => {
207+
let params = self.ast.formal_parameters(
208+
SPAN,
209+
FormalParameterKind::Signature,
210+
self.ast.new_vec(),
211+
None,
212+
);
213+
self.transform_class_method_definition(method, params, None)
214+
}
215+
MethodDefinitionKind::Set => {
216+
let params = self.create_formal_parameters(
217+
self.ast.binding_pattern_identifier(BindingIdentifier::new(
218+
SPAN,
219+
self.ast.new_atom("value"),
220+
)),
221+
None,
222+
);
223+
self.transform_class_method_definition(method, params, None)
224+
}
225+
MethodDefinitionKind::Constructor => {
226+
unreachable!()
227+
}
228+
}
229+
}
230+
231+
fn transform_constructor_params_to_class_properties(
232+
&self,
233+
function: &Function<'a>,
234+
params: &FormalParameters<'a>,
235+
) -> oxc_allocator::Vec<'a, ClassElement<'a>> {
236+
let mut elements = self.ast.new_vec();
237+
for (index, param) in function.params.items.iter().enumerate() {
238+
if param.accessibility.is_some() {
239+
// transformed params will definitely have type annotation
240+
let type_annotation = self.ast.copy(&params.items[index].pattern.type_annotation);
241+
if let Some(new_element) =
242+
self.transform_formal_parameter_to_class_property(param, type_annotation)
243+
{
244+
elements.push(new_element);
245+
}
246+
}
247+
}
248+
elements
249+
}
250+
205251
pub fn transform_class(&self, decl: &Class<'a>) -> Option<Box<'a, Class<'a>>> {
206252
if decl.is_declare() {
207253
return None;
@@ -220,89 +266,95 @@ impl<'a> IsolatedDeclarations<'a> {
220266
}
221267
}
222268

223-
let mut elements = self.ast.new_vec();
224-
let mut has_private_key = false;
225-
let mut accessor_return_types: FxHashMap<&Atom<'a>, Option<Box<'a, TSTypeAnnotation<'a>>>> =
269+
let mut inferred_accessor_type: FxHashMap<Atom<'a>, Box<'a, TSTypeAnnotation<'a>>> =
226270
FxHashMap::default();
227271

228-
// Transform get accessor first, and collect return type.
229-
// The return type will be used to infer the type of the set accessor.
272+
// Infer get accessor return type from set accessor
273+
// Infer set accessor parameter type from get accessor
230274
for element in &decl.body.body {
231275
if let ClassElement::MethodDefinition(method) = element {
232-
if method.key.is_private_identifier() {
233-
has_private_key = true;
276+
if method.key.is_private_identifier()
277+
|| method.accessibility.is_some_and(|a| a.is_private())
278+
|| (method.computed && !self.is_literal_key(&method.key))
279+
{
234280
continue;
235281
}
236-
if self.report_property_key(&method.key, method.computed) {
282+
let Some(name) = method.key.static_name() else {
283+
continue;
284+
};
285+
let name = self.ast.new_atom(&name);
286+
if inferred_accessor_type.contains_key(&name) {
287+
// We've inferred that accessor type already
237288
continue;
238289
}
239-
240-
if method.kind.is_get() {
241-
if let PropertyKey::StaticIdentifier(ident) = &method.key {
242-
let function = &method.value;
243-
let params = self.transform_formal_parameters(&function.params);
290+
let function = &method.value;
291+
match method.kind {
292+
MethodDefinitionKind::Get => {
244293
let return_type = self.infer_function_return_type(function);
245-
if return_type.is_none() {
246-
self.error(accessor_must_have_explicit_return_type(method.key.span()));
294+
if let Some(return_type) = return_type {
295+
inferred_accessor_type.insert(name, self.ast.copy(&return_type));
247296
}
248-
accessor_return_types.insert(&ident.name, self.ast.copy(&return_type));
249-
let element =
250-
self.transform_class_method_definition(method, params, return_type);
251-
elements.push(element);
252-
continue;
253297
}
298+
MethodDefinitionKind::Set => {
299+
if let Some(param) = function.params.items.first() {
300+
let type_annotation =
301+
param.pattern.type_annotation.as_ref().map_or_else(
302+
|| {
303+
self.infer_type_from_formal_parameter(param)
304+
.map(|x| self.ast.ts_type_annotation(SPAN, x))
305+
},
306+
|t| Some(self.ast.copy(t)),
307+
);
308+
if let Some(type_annotation) = type_annotation {
309+
inferred_accessor_type.insert(name, type_annotation);
310+
}
311+
}
312+
}
313+
_ => {}
254314
}
255315
}
256-
elements.push(self.ast.copy(element));
257316
}
258317

259-
let mut new_elements = self.ast.new_vec();
260-
for element in elements.drain(..) {
318+
let mut has_private_key = false;
319+
let mut elements = self.ast.new_vec();
320+
for element in &decl.body.body {
261321
match element {
262322
ClassElement::StaticBlock(_) => {}
263323
ClassElement::MethodDefinition(ref method) => {
264-
// Transformed in the first loop
265-
if method.kind.is_get() {
266-
new_elements.push(element);
267-
continue;
268-
}
269324
if method.key.is_private_identifier() {
270325
has_private_key = true;
271326
continue;
272327
}
273328
if self.report_property_key(&method.key, method.computed) {
274329
continue;
275330
}
331+
if method.accessibility.is_some_and(|a| a.is_private()) {
332+
elements.push(self.transform_private_modifier_method(method));
333+
continue;
334+
}
276335
let function = &method.value;
277336
let params = if method.kind.is_set() {
278-
if let PropertyKey::StaticIdentifier(ident) = &method.key {
279-
self.transform_set_accessor_params(
280-
&function.params,
281-
accessor_return_types.remove(&ident.name).unwrap_or_default(),
282-
)
283-
} else {
284-
self.transform_formal_parameters(&function.params)
285-
}
337+
method.key.static_name().map_or_else(
338+
|| self.transform_formal_parameters(&function.params),
339+
|n| {
340+
self.transform_set_accessor_params(
341+
&function.params,
342+
inferred_accessor_type
343+
.get(&self.ast.new_atom(&n))
344+
.map(|t| self.ast.copy(t)),
345+
)
346+
},
347+
)
286348
} else {
287349
self.transform_formal_parameters(&function.params)
288350
};
289351

290352
if let MethodDefinitionKind::Constructor = method.kind {
291-
for (index, param) in function.params.items.iter().enumerate() {
292-
if param.accessibility.is_some() {
293-
// transformed params will definitely have type annotation
294-
let type_annotation =
295-
self.ast.copy(&params.items[index].pattern.type_annotation);
296-
if let Some(new_element) = self
297-
.transform_formal_parameter_to_class_property(
298-
param,
299-
type_annotation,
300-
)
301-
{
302-
new_elements.push(new_element);
303-
}
304-
}
305-
}
353+
elements.extend(
354+
self.transform_constructor_params_to_class_properties(
355+
function, &params,
356+
),
357+
);
306358
}
307359

308360
let return_type = match method.kind {
@@ -315,14 +367,24 @@ impl<'a> IsolatedDeclarations<'a> {
315367
}
316368
rt
317369
}
318-
MethodDefinitionKind::Set | MethodDefinitionKind::Constructor => None,
319370
MethodDefinitionKind::Get => {
320-
unreachable!("get accessor should be transformed in the first loop")
371+
let rt = method.key.static_name().and_then(|name| {
372+
inferred_accessor_type
373+
.get(&self.ast.new_atom(&name))
374+
.map(|t| self.ast.copy(t))
375+
});
376+
if rt.is_none() {
377+
self.error(accessor_must_have_explicit_return_type(
378+
method.key.span(),
379+
));
380+
}
381+
rt
321382
}
383+
MethodDefinitionKind::Set | MethodDefinitionKind::Constructor => None,
322384
};
323385
let new_element =
324386
self.transform_class_method_definition(method, params, return_type);
325-
new_elements.push(new_element);
387+
elements.push(new_element);
326388
}
327389
ClassElement::PropertyDefinition(property) => {
328390
if self.report_property_key(&property.key, property.computed) {
@@ -332,7 +394,7 @@ impl<'a> IsolatedDeclarations<'a> {
332394
if property.key.is_private_identifier() {
333395
has_private_key = true;
334396
} else {
335-
new_elements.push(self.transform_class_property_definition(&property));
397+
elements.push(self.transform_class_property_definition(property));
336398
}
337399
}
338400
ClassElement::AccessorProperty(property) => {
@@ -355,9 +417,9 @@ impl<'a> IsolatedDeclarations<'a> {
355417
property.r#static,
356418
self.ast.new_vec(),
357419
);
358-
new_elements.push(new_element);
420+
elements.push(new_element);
359421
}
360-
ClassElement::TSIndexSignature(_) => new_elements.push(element),
422+
ClassElement::TSIndexSignature(_) => elements.push(self.ast.copy(element)),
361423
}
362424
}
363425

@@ -375,10 +437,10 @@ impl<'a> IsolatedDeclarations<'a> {
375437
None, decorators,
376438
);
377439

378-
new_elements.insert(0, element);
440+
elements.insert(0, element);
379441
}
380442

381-
let body = self.ast.class_body(decl.body.span, new_elements);
443+
let body = self.ast.class_body(decl.body.span, elements);
382444

383445
let mut modifiers = self.modifiers_declare();
384446
if decl.modifiers.is_contains_abstract() {

0 commit comments

Comments
 (0)