Skip to content

Commit 57cd5a9

Browse files
committed
fix(isolated-declarations): incorrect type of object property accessor
1 parent 46e9d6f commit 57cd5a9

File tree

3 files changed

+159
-16
lines changed

3 files changed

+159
-16
lines changed

crates/oxc_isolated_declarations/src/types.rs

Lines changed: 79 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ use oxc_ast::{
33
NONE,
44
ast::{
55
ArrayExpression, ArrayExpressionElement, ArrowFunctionExpression, Expression, Function,
6-
ObjectExpression, ObjectPropertyKind, TSLiteral, TSMethodSignatureKind, TSTupleElement,
7-
TSType, TSTypeOperatorOperator,
6+
ObjectExpression, ObjectPropertyKind, PropertyKey, PropertyKind, TSLiteral,
7+
TSMethodSignatureKind, TSTupleElement, TSType, TSTypeOperatorOperator,
88
},
99
};
10-
use oxc_span::{GetSpan, SPAN, Span};
10+
use oxc_span::{ContentEq, GetSpan, SPAN, Span};
1111

1212
use crate::{
1313
IsolatedDeclarations,
@@ -81,6 +81,12 @@ impl<'a> IsolatedDeclarations<'a> {
8181
expr: &ObjectExpression<'a>,
8282
is_const: bool,
8383
) -> TSType<'a> {
84+
// The span of accessors that cannot infer the type.
85+
let mut accessor_spans = Vec::new();
86+
// One of setter or getter is inferred, then the PropertyKey will be added to it.
87+
// Use `Vec` rather than `HashSet` is because the `PropertyKey` is not satisfied with `Hash` trait,
88+
let mut accessor_inferred: Vec<PropertyKey<'a>> = Vec::new();
89+
8490
let members =
8591
self.ast.vec_from_iter(expr.properties.iter().filter_map(|property| match property {
8692
ObjectPropertyKind::ObjectProperty(object) => {
@@ -111,26 +117,76 @@ impl<'a> IsolatedDeclarations<'a> {
111117
}
112118
}
113119

114-
let type_annotation = if is_const {
115-
self.transform_expression_to_ts_type(&object.value)
116-
} else {
117-
self.infer_type_from_expression(&object.value)
118-
};
120+
let key = object.key.clone_in(self.ast.allocator);
119121

120-
if type_annotation.is_none() {
121-
self.error(inferred_type_of_expression(object.value.span()));
122-
return None;
123-
}
122+
let type_annotation = match object.kind {
123+
PropertyKind::Get => {
124+
if accessor_inferred.iter().any(|k| k.content_eq(&key)) {
125+
return None;
126+
}
127+
128+
let Expression::FunctionExpression(function) = &object.value else {
129+
unreachable!(
130+
"`object.kind` being `Get` guarantees that it is a function"
131+
);
132+
};
133+
134+
let annotation = self.infer_function_return_type(function);
135+
if annotation.is_none() {
136+
accessor_spans.push((key.clone_in(self.ast.allocator), key.span()));
137+
return None;
138+
}
139+
140+
accessor_inferred.push(key.clone_in(self.ast.allocator));
141+
annotation
142+
}
143+
PropertyKind::Set => {
144+
if accessor_inferred.iter().any(|k| k.content_eq(&key)) {
145+
return None;
146+
}
147+
148+
let Expression::FunctionExpression(function) = &object.value else {
149+
unreachable!(
150+
"`object.kind` being `Set` guarantees that it is a function"
151+
);
152+
};
153+
let annotation = function.params.items.first().and_then(|param| {
154+
param.pattern.type_annotation.clone_in(self.ast.allocator)
155+
});
156+
if annotation.is_none() {
157+
accessor_spans
158+
.push((key.clone_in(self.ast.allocator), function.params.span));
159+
return None;
160+
}
161+
162+
accessor_inferred.push(key.clone_in(self.ast.allocator));
163+
annotation
164+
}
165+
PropertyKind::Init => {
166+
let type_annotation = if is_const {
167+
self.transform_expression_to_ts_type(&object.value)
168+
} else {
169+
self.infer_type_from_expression(&object.value)
170+
};
171+
172+
if type_annotation.is_none() {
173+
self.error(inferred_type_of_expression(object.value.span()));
174+
return None;
175+
}
176+
177+
type_annotation.map(|type_annotation| {
178+
self.ast.alloc_ts_type_annotation(SPAN, type_annotation)
179+
})
180+
}
181+
};
124182

125183
let property_signature = self.ast.ts_signature_property_signature(
126184
object.span,
127185
false,
128186
false,
129187
is_const,
130-
object.key.clone_in(self.ast.allocator),
131-
type_annotation.map(|type_annotation| {
132-
self.ast.ts_type_annotation(SPAN, type_annotation)
133-
}),
188+
key,
189+
type_annotation,
134190
);
135191
Some(property_signature)
136192
}
@@ -139,6 +195,13 @@ impl<'a> IsolatedDeclarations<'a> {
139195
None
140196
}
141197
}));
198+
199+
for (key, span) in accessor_spans {
200+
if !accessor_inferred.iter().any(|k| k.content_eq(&key)) {
201+
self.error(inferred_type_of_expression(span));
202+
}
203+
}
204+
142205
self.ast.ts_type_type_literal(SPAN, members)
143206
}
144207

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
const A = {
2+
get state(): number {
3+
return 0;
4+
},
5+
};
6+
7+
const B = {
8+
set state(v: string) {
9+
// do something
10+
},
11+
};
12+
13+
const C = {
14+
get state(): number {
15+
return 0;
16+
},
17+
set state(v: number) {
18+
// do something
19+
},
20+
};
21+
22+
const D = {
23+
get state(): number {
24+
return 0;
25+
},
26+
set state(v: string) {
27+
// do something
28+
},
29+
};
30+
31+
const E = {
32+
get state() {
33+
return A;
34+
},
35+
set state(v) {
36+
// do something
37+
},
38+
};
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
source: crates/oxc_isolated_declarations/tests/mod.rs
3+
input_file: crates/oxc_isolated_declarations/tests/fixtures/object.ts
4+
---
5+
```
6+
==================== .D.TS ====================
7+
8+
declare const A: {
9+
state: number
10+
};
11+
declare const B: {
12+
state: string
13+
};
14+
declare const C: {
15+
state: number
16+
};
17+
declare const D: {
18+
state: number
19+
};
20+
declare const E: {};
21+
22+
23+
==================== Errors ====================
24+
25+
x TS9013: Expression type can't be inferred with --isolatedDeclarations.
26+
,-[32:6]
27+
31 | const E = {
28+
32 | get state() {
29+
: ^^^^^
30+
33 | return A;
31+
`----
32+
33+
x TS9013: Expression type can't be inferred with --isolatedDeclarations.
34+
,-[35:11]
35+
34 | },
36+
35 | set state(v) {
37+
: ^^^
38+
36 | // do something
39+
`----
40+
41+
42+
```

0 commit comments

Comments
 (0)