1use rustc_hash::FxHashMap;
2use swc_atoms::atom;
3use swc_common::{util::take::Take, Spanned, SyntaxContext, DUMMY_SP};
4use swc_ecma_ast::{
5 Accessibility, BindingIdent, Class, ClassMember, ClassProp, Expr, Key, Lit, MethodKind, Param,
6 ParamOrTsParamProp, Pat, PrivateName, PrivateProp, PropName, TsParamProp, TsParamPropParam,
7 TsTypeAnn,
8};
9
10use super::{
11 type_ann,
12 util::ast_ext::{ExprExit, PatExt, PropNameExit, StaticProp},
13 FastDts,
14};
15
16impl FastDts {
17 pub(crate) fn transform_class(&mut self, class: &mut Class) {
18 if let Some(super_class) = &class.super_class {
19 let is_not_allowed = super_class.get_root_ident().is_none();
20 if is_not_allowed {
21 self.extends_clause_expression(super_class.span());
22 }
23 }
24
25 let setter_getter_annotations = self.collect_getter_or_setter_annotations(class);
26 let mut is_function_overloads = false;
27 let mut has_private_key = false;
28 let body = class.body.take();
29
30 for mut member in body {
31 match &mut member {
32 ClassMember::Constructor(constructor) => {
33 if self.has_internal_annotation(constructor.span_lo()) {
34 continue;
35 }
36
37 let private_constructor =
38 constructor.accessibility == Some(Accessibility::Private);
39
40 class.body.splice(
42 0..0,
43 self.transform_constructor_params(
44 &mut constructor.params,
45 private_constructor,
46 ),
47 );
48
49 if !(constructor.is_optional) && constructor.body.is_none() {
50 is_function_overloads = true;
51 } else if is_function_overloads {
52 is_function_overloads = false;
53 continue;
54 }
55
56 if private_constructor {
57 constructor.params.clear();
58 }
59
60 constructor.body = None;
61 constructor.accessibility =
62 self.transform_accessibility(constructor.accessibility);
63 class.body.push(member);
64 }
65 ClassMember::Method(method) => {
66 if self.has_internal_annotation(method.span_lo()) {
67 continue;
68 }
69 if !(method.is_abstract || method.is_optional) && method.function.body.is_none()
70 {
71 is_function_overloads = true;
72 } else if is_function_overloads {
73 is_function_overloads = false;
74 continue;
75 }
76
77 if self.report_property_key(&method.key) {
78 continue;
79 }
80
81 match method.kind {
83 MethodKind::Method => {
84 if method.accessibility.is_some_and(|accessibility| {
85 accessibility == Accessibility::Private
86 }) {
87 class.body.push(ClassMember::ClassProp(ClassProp {
88 span: method.span,
89 key: method.key.clone(),
90 value: None,
91 type_ann: None,
92 is_static: method.is_static,
93 decorators: Vec::new(),
94 accessibility: self
95 .transform_accessibility(method.accessibility),
96 is_abstract: method.is_abstract,
97 is_optional: method.is_optional,
98 is_override: false,
99 readonly: false,
100 declare: false,
101 definite: false,
102 }));
103 continue;
104 }
105 self.transform_fn_params(&mut method.function.params);
106 }
107 MethodKind::Getter => {
108 if method.accessibility.is_some_and(|accessibility| {
109 accessibility == Accessibility::Private
110 }) {
111 method.function.params.clear();
112 method.function.return_type = None;
113 method.function.decorators.clear();
114 method.function.body = None;
115 method.function.is_generator = false;
116 method.function.is_async = false;
117 method.accessibility =
118 self.transform_accessibility(method.accessibility);
119 class.body.push(member);
120 continue;
121 }
122 self.transform_fn_params(&mut method.function.params);
123 }
124 MethodKind::Setter => {
125 if method.accessibility.is_some_and(|accessibility| {
126 accessibility == Accessibility::Private
127 }) {
128 method.function.params = vec![Param {
129 span: DUMMY_SP,
130 decorators: Vec::new(),
131 pat: Pat::Ident(BindingIdent {
132 id: atom!("value").into(),
133 type_ann: None,
134 }),
135 }];
136 method.function.decorators.clear();
137 method.function.body = None;
138 method.function.is_generator = false;
139 method.function.is_async = false;
140 method.function.return_type = None;
141 method.accessibility =
142 self.transform_accessibility(method.accessibility);
143 class.body.push(member);
144 continue;
145 }
146
147 if method.function.params.is_empty() {
148 method.function.params.push(Param {
149 span: DUMMY_SP,
150 decorators: Vec::new(),
151 pat: Pat::Ident(BindingIdent {
152 id: atom!("value").into(),
153 type_ann: None,
154 }),
155 });
156 } else {
157 method.function.params.truncate(1);
158 let param = method.function.params.first_mut().unwrap();
159 let static_prop = method.key.static_prop(self.unresolved_mark);
160
161 if let Some(type_ann) = static_prop
162 .and_then(|prop| setter_getter_annotations.get(&prop))
163 {
164 param.pat.set_type_ann(Some(type_ann.clone()));
165 }
166 }
167 }
168 #[cfg(swc_ast_unknown)]
169 _ => panic!("unable to access unknown nodes"),
170 }
171
172 match method.kind {
174 MethodKind::Method => {
175 self.transform_fn_return_type(&mut method.function);
176 if method.function.return_type.is_none() {
177 self.method_must_have_explicit_return_type(method.key.span());
178 }
179 }
180 MethodKind::Getter => {
181 self.transform_fn_return_type(&mut method.function);
182 if method.function.return_type.is_none() {
183 method.function.return_type = method
184 .key
185 .static_prop(self.unresolved_mark)
186 .and_then(|prop| setter_getter_annotations.get(&prop))
187 .cloned();
188 }
189 if method.function.return_type.is_none() {
190 self.accessor_must_have_explicit_return_type(method.key.span());
191 }
192 }
193 MethodKind::Setter => method.function.return_type = None,
194 #[cfg(swc_ast_unknown)]
195 _ => panic!("unable to access unknown nodes"),
196 }
197
198 method.function.body = None;
199 method.function.is_async = false;
200 method.function.is_generator = false;
201 method.accessibility = self.transform_accessibility(method.accessibility);
202 class.body.push(member);
203 }
204 ClassMember::ClassProp(prop) => {
205 if self.has_internal_annotation(prop.span_lo()) {
206 continue;
207 }
208 if self.report_property_key(&prop.key) {
209 continue;
210 }
211
212 self.transform_class_property(prop);
213 class.body.push(member);
214 }
215 ClassMember::PrivateMethod(_) | ClassMember::PrivateProp(_) => {
216 has_private_key = true;
217 }
218 ClassMember::TsIndexSignature(ts_index_signature) => {
219 if self.has_internal_annotation(ts_index_signature.span_lo()) {
220 continue;
221 }
222 class.body.push(member);
223 }
224 ClassMember::AutoAccessor(auto_accessor) => {
225 if self.has_internal_annotation(auto_accessor.span_lo()) {
226 continue;
227 }
228 let Key::Public(prop_name) = &auto_accessor.key else {
229 has_private_key = true;
230 continue;
231 };
232
233 if self.report_property_key(prop_name) {
234 continue;
235 }
236
237 if auto_accessor
238 .accessibility
239 .is_some_and(|accessibility| accessibility == Accessibility::Private)
240 {
241 auto_accessor.decorators.clear();
242 auto_accessor.definite = false;
243 auto_accessor.type_ann = None;
244 auto_accessor.accessibility =
245 self.transform_accessibility(auto_accessor.accessibility);
246 }
247
248 auto_accessor.is_override = false;
249 auto_accessor.value = None;
250 class.body.push(member);
251 }
252 ClassMember::Empty(_) | ClassMember::StaticBlock(_) => {}
253 #[cfg(swc_ast_unknown)]
254 _ => panic!("unable to access unknown nodes"),
255 }
256 }
257
258 if has_private_key {
259 class.body.insert(
260 0,
261 ClassMember::PrivateProp(PrivateProp {
262 span: DUMMY_SP,
263 ctxt: SyntaxContext::empty(),
264 key: PrivateName {
265 span: DUMMY_SP,
266 name: atom!("private"),
267 },
268 value: None,
269 type_ann: None,
270 is_static: false,
271 decorators: Vec::new(),
272 accessibility: None,
273 is_optional: false,
274 is_override: false,
275 readonly: false,
276 definite: false,
277 }),
278 );
279 }
280 }
281
282 pub(crate) fn transform_constructor_params(
283 &mut self,
284 params: &mut [ParamOrTsParamProp],
285 private_constructor: bool,
286 ) -> Vec<ClassMember> {
287 let mut is_required = false;
288 let mut private_properties = Vec::new();
289 for param in params.iter_mut().rev() {
290 match param {
291 ParamOrTsParamProp::TsParamProp(ts_param_prop) => {
292 is_required |= match &ts_param_prop.param {
293 TsParamPropParam::Ident(binding_ident) => !binding_ident.optional,
294 TsParamPropParam::Assign(_) => false,
295 #[cfg(swc_ast_unknown)]
296 _ => panic!("unable to access unknown nodes"),
297 };
298 if let Some(private_prop) =
299 self.transform_constructor_ts_param(ts_param_prop, is_required)
300 {
301 private_properties.push(private_prop);
302 }
303 ts_param_prop.readonly = false;
304 ts_param_prop.accessibility = None;
305 }
306 ParamOrTsParamProp::Param(param) => {
307 if private_constructor {
308 continue;
309 }
310
311 self.transform_fn_param(param, is_required);
312 is_required |= match ¶m.pat {
313 Pat::Ident(binding_ident) => !binding_ident.optional,
314 Pat::Array(array_pat) => !array_pat.optional,
315 Pat::Object(object_pat) => !object_pat.optional,
316 Pat::Assign(_) | Pat::Invalid(_) | Pat::Expr(_) | Pat::Rest(_) => false,
317 #[cfg(swc_ast_unknown)]
318 _ => panic!("unable to access unknown nodes"),
319 }
320 }
321 #[cfg(swc_ast_unknown)]
322 _ => panic!("unable to access unknown nodes"),
323 }
324 }
325 private_properties.reverse();
326 private_properties
327 }
328
329 pub(crate) fn transform_constructor_ts_param(
330 &mut self,
331 ts_param_prop: &mut TsParamProp,
332 is_required: bool,
333 ) -> Option<ClassMember> {
334 if let TsParamPropParam::Assign(assign_pat) = &mut ts_param_prop.param {
337 if self.check_assign_pat_param(assign_pat) {
338 self.parameter_must_have_explicit_type(ts_param_prop.span);
339 return None;
340 }
341 }
342
343 let (is_optional, type_ann) = match &mut ts_param_prop.param {
345 TsParamPropParam::Ident(binding_ident) => {
346 if binding_ident.type_ann.is_none() {
347 self.parameter_must_have_explicit_type(ts_param_prop.span);
348 }
349
350 (binding_ident.optional, &mut binding_ident.type_ann)
351 }
352 TsParamPropParam::Assign(assign_pat) => {
353 let is_optional = match assign_pat.left.as_ref() {
355 Pat::Ident(ident) => ident.optional,
356 Pat::Array(array_pat) => array_pat.optional,
357 Pat::Object(object_pat) => object_pat.optional,
358 _ => false,
359 };
360
361 if !self.transform_assign_pat(assign_pat, is_required) {
362 self.parameter_must_have_explicit_type(ts_param_prop.span);
363 }
364
365 (
366 is_optional,
367 match assign_pat.left.as_mut() {
368 Pat::Ident(ident) => &mut ident.type_ann,
369 Pat::Array(array_pat) => &mut array_pat.type_ann,
370 Pat::Object(object_pat) => &mut object_pat.type_ann,
371 Pat::Assign(_) | Pat::Rest(_) | Pat::Invalid(_) | Pat::Expr(_) => {
372 return None
373 }
374 #[cfg(swc_ast_unknown)]
375 _ => panic!("unable to access unknown nodes"),
376 },
377 )
378 }
379 #[cfg(swc_ast_unknown)]
380 _ => panic!("unable to access unknown nodes"),
381 };
382
383 if let Some(type_ann) = type_ann {
385 if is_optional && self.add_undefined_type_for_param(type_ann) {
386 self.implicitly_adding_undefined_to_type(ts_param_prop.span);
387 }
388 }
389
390 if let Some(assign_pat) = ts_param_prop.param.as_assign() {
392 if let Some(ident) = assign_pat.left.as_ident() {
394 ts_param_prop.param = TsParamPropParam::Ident(ident.clone());
395 }
396 }
397
398 let ident = (match &ts_param_prop.param {
400 TsParamPropParam::Ident(binding_ident) => Some(binding_ident.id.clone()),
401 TsParamPropParam::Assign(assign_pat) => assign_pat
402 .left
403 .as_ident()
404 .map(|binding_ident| binding_ident.id.clone()),
405 #[cfg(swc_ast_unknown)]
406 _ => panic!("unable to access unknown nodes"),
407 })?;
408
409 let type_ann = if ts_param_prop
410 .accessibility
411 .is_some_and(|accessibility| accessibility == Accessibility::Private)
412 {
413 None
414 } else {
415 match &ts_param_prop.param {
416 TsParamPropParam::Ident(binding_ident) => binding_ident.type_ann.clone(),
417 TsParamPropParam::Assign(assign_pat) => match assign_pat.left.as_ref() {
418 Pat::Ident(binding_ident) => binding_ident.type_ann.clone(),
419 Pat::Array(array_pat) => array_pat.type_ann.clone(),
420 Pat::Object(object_pat) => object_pat.type_ann.clone(),
421 Pat::Assign(_) | Pat::Rest(_) | Pat::Invalid(_) | Pat::Expr(_) => None,
422 #[cfg(swc_ast_unknown)]
423 _ => panic!("unable to access unknown nodes"),
424 },
425 #[cfg(swc_ast_unknown)]
426 _ => panic!("unable to access unknown nodes"),
427 }
428 };
429
430 Some(ClassMember::ClassProp(ClassProp {
431 span: DUMMY_SP,
432 key: PropName::Ident(ident.into()),
433 value: None,
434 type_ann,
435 is_static: false,
436 decorators: Vec::new(),
437 accessibility: self.transform_accessibility(ts_param_prop.accessibility),
438 is_abstract: false,
439 is_optional,
440 is_override: ts_param_prop.is_override,
441 readonly: ts_param_prop.readonly,
442 declare: false,
443 definite: false,
444 }))
445 }
446
447 pub(crate) fn transform_class_property(&mut self, prop: &mut ClassProp) {
448 if prop.accessibility != Some(Accessibility::Private) {
449 if prop.type_ann.is_none() {
450 if let Some(value) = prop.value.as_ref() {
451 if prop.readonly {
452 if Self::need_to_infer_type_from_expression(value) {
453 prop.type_ann = self.transform_expr_to_ts_type(value).map(type_ann);
454 prop.value = None;
455 } else if let Some(tpl) = value.as_tpl() {
456 prop.value = self
457 .tpl_to_string(tpl)
458 .map(|s| Box::new(Expr::Lit(Lit::Str(s))));
459 }
460 } else {
461 prop.type_ann = self.infer_type_from_expr(value).map(type_ann);
462 prop.value = None;
463 }
464 }
465 } else {
466 prop.value = None;
467 }
468
469 if prop.type_ann.is_none() && prop.value.is_none() {
470 self.property_must_have_explicit_type(prop.key.span());
471 }
472 } else {
473 prop.type_ann = None;
474 prop.value = None;
475 }
476
477 prop.declare = false;
478 prop.decorators.clear();
479 prop.accessibility = self.transform_accessibility(prop.accessibility);
480 }
481
482 pub(crate) fn transform_accessibility(
483 &mut self,
484 accessibility: Option<Accessibility>,
485 ) -> Option<Accessibility> {
486 if accessibility.is_none()
487 || accessibility.is_some_and(|accessibility| accessibility == Accessibility::Public)
488 {
489 None
490 } else {
491 accessibility
492 }
493 }
494
495 pub(crate) fn collect_getter_or_setter_annotations(
496 &mut self,
497 class: &Class,
498 ) -> FxHashMap<StaticProp, Box<TsTypeAnn>> {
499 let mut annotations = FxHashMap::default();
500 for member in &class.body {
501 let ClassMember::Method(method) = member else {
502 continue;
503 };
504
505 if method
506 .accessibility
507 .is_some_and(|accessibility| accessibility == Accessibility::Private)
508 || (method
509 .key
510 .as_computed()
511 .is_some_and(|computed| Self::is_literal(&computed.expr)))
512 {
513 continue;
514 }
515
516 let Some(static_prop) = method.key.static_prop(self.unresolved_mark) else {
517 continue;
518 };
519
520 match method.kind {
521 MethodKind::Getter => {
522 if let Some(type_ann) = method
523 .function
524 .return_type
525 .clone()
526 .or_else(|| self.infer_function_return_type(&method.function))
527 {
528 annotations.insert(static_prop, type_ann);
529 }
530 }
531 MethodKind::Setter => {
532 let Some(first_param) = method.function.params.first() else {
533 continue;
534 };
535
536 if let Some(type_ann) = first_param.pat.get_type_ann() {
537 annotations.insert(static_prop, type_ann.clone());
538 }
539 }
540 _ => continue,
541 }
542 }
543
544 annotations
545 }
546
547 pub(crate) fn report_property_key(&mut self, key: &PropName) -> bool {
548 if let Some(computed) = key.as_computed() {
549 let is_symbol = self.is_global_symbol_object(&computed.expr);
550 let is_literal = Self::is_literal(&computed.expr);
551 if !is_symbol && !is_literal {
552 self.computed_property_name(key.span());
553 }
554 return !is_symbol && !is_literal;
555 }
556 false
557 }
558
559 pub(crate) fn is_global_symbol_object(&self, expr: &Expr) -> bool {
560 expr.get_global_symbol_prop(self.unresolved_mark).is_some()
561 }
562}