swc_typescript/fast_dts/
function.rs

1use std::mem;
2
3use swc_atoms::Atom;
4use swc_common::{Span, Spanned, DUMMY_SP};
5use swc_ecma_ast::{
6    AssignPat, Decl, ExportDecl, Function, ModuleDecl, ModuleItem, Param, Pat, Script, Stmt,
7    TsKeywordTypeKind, TsParenthesizedType, TsType, TsTypeAnn, TsUnionOrIntersectionType,
8    TsUnionType,
9};
10
11use super::{
12    type_ann,
13    util::types::{any_type_ann, ts_keyword_type},
14    FastDts,
15};
16
17impl FastDts {
18    pub(crate) fn transform_fn(&mut self, func: &mut Function, ident_span: Option<Span>) {
19        self.transform_fn_return_type(func);
20        if func.return_type.is_none() {
21            self.function_must_have_explicit_return_type(
22                ident_span.unwrap_or_else(|| Span::new(func.span_lo(), func.body.span_lo())),
23            );
24        }
25        self.transform_fn_params(&mut func.params);
26        func.is_async = false;
27        func.is_generator = false;
28        func.body = None
29    }
30
31    pub(crate) fn transform_fn_return_type(&mut self, func: &mut Function) {
32        if func.return_type.is_none() && !func.is_async && !func.is_generator {
33            func.return_type = self.infer_function_return_type(func);
34        }
35    }
36
37    pub(crate) fn transform_fn_params(&mut self, params: &mut [Param]) {
38        // If there is required param after current param.
39        let mut is_required = false;
40        for param in params.iter_mut().rev() {
41            is_required |= match &param.pat {
42                Pat::Ident(binding_ident) => !binding_ident.optional,
43                Pat::Array(array_pat) => !array_pat.optional,
44                Pat::Object(object_pat) => !object_pat.optional,
45                Pat::Assign(_) | Pat::Invalid(_) | Pat::Expr(_) | Pat::Rest(_) => false,
46                #[cfg(swc_ast_unknown)]
47                _ => panic!("unable to access unknown nodes"),
48            };
49            self.transform_fn_param(param, is_required);
50        }
51    }
52
53    pub(crate) fn transform_fn_param(&mut self, param: &mut Param, is_required: bool) {
54        // 1. Check assign pat type
55        if let Pat::Assign(assign_pat) = &mut param.pat {
56            if self.check_assign_pat_param(assign_pat) {
57                self.parameter_must_have_explicit_type(param.span);
58                return;
59            }
60        }
61
62        // 2. Infer type annotation
63        let (should_add_undefined, type_ann) = match &mut param.pat {
64            Pat::Ident(ident) => {
65                if ident.type_ann.is_none() {
66                    self.parameter_must_have_explicit_type(param.span);
67                }
68                (ident.type_ann.is_none(), ident.type_ann.as_mut())
69            }
70            Pat::Array(arr_pat) => {
71                if arr_pat.type_ann.is_none() {
72                    self.parameter_must_have_explicit_type(param.span);
73                }
74                (arr_pat.type_ann.is_none(), arr_pat.type_ann.as_mut())
75            }
76            Pat::Object(obj_pat) => {
77                if obj_pat.type_ann.is_none() {
78                    self.parameter_must_have_explicit_type(param.span);
79                }
80                (obj_pat.type_ann.is_none(), obj_pat.type_ann.as_mut())
81            }
82            Pat::Assign(assign_pat) => {
83                if !self.transform_assign_pat(assign_pat, is_required) {
84                    self.parameter_must_have_explicit_type(param.span);
85                }
86
87                (
88                    true,
89                    match assign_pat.left.as_mut() {
90                        Pat::Ident(ident) => ident.type_ann.as_mut(),
91                        Pat::Array(array_pat) => array_pat.type_ann.as_mut(),
92                        Pat::Object(object_pat) => object_pat.type_ann.as_mut(),
93                        Pat::Assign(_) | Pat::Rest(_) | Pat::Invalid(_) | Pat::Expr(_) => return,
94                        #[cfg(swc_ast_unknown)]
95                        _ => panic!("unable to access unknown nodes"),
96                    },
97                )
98            }
99            Pat::Rest(_) | Pat::Expr(_) | Pat::Invalid(_) => (false, None),
100            #[cfg(swc_ast_unknown)]
101            _ => panic!("unable to access unknown nodes"),
102        };
103
104        // 3. Add undefined type if needed
105        if let Some(type_ann) = type_ann {
106            if is_required && should_add_undefined && self.add_undefined_type_for_param(type_ann) {
107                self.implicitly_adding_undefined_to_type(param.span);
108            }
109        }
110
111        // 4. Flat param pat
112        let pat = mem::take(&mut param.pat);
113        param.pat = match pat {
114            Pat::Assign(assign_pat) => *assign_pat.left,
115            _ => pat,
116        };
117    }
118
119    pub(crate) fn transform_assign_pat(
120        &mut self,
121        assign_pat: &mut AssignPat,
122        is_required: bool,
123    ) -> bool {
124        // We can only infer types from the right expr of assign pat
125        let left_type_ann = match assign_pat.left.as_mut() {
126            Pat::Ident(ident) => {
127                ident.optional |= !is_required;
128                &mut ident.type_ann
129            }
130            Pat::Array(array_pat) => {
131                array_pat.optional |= !is_required;
132                &mut array_pat.type_ann
133            }
134            Pat::Object(object_pat) => {
135                object_pat.optional |= !is_required;
136                &mut object_pat.type_ann
137            }
138            // These are illegal
139            Pat::Assign(_) | Pat::Rest(_) | Pat::Invalid(_) | Pat::Expr(_) => return true,
140            #[cfg(swc_ast_unknown)]
141            _ => panic!("unable to access unknown nodes"),
142        };
143
144        let mut has_expclicit_type = true;
145        if left_type_ann.is_none() {
146            *left_type_ann = self
147                .infer_type_from_expr(&assign_pat.right)
148                .map(type_ann)
149                .or_else(|| {
150                    has_expclicit_type = false;
151                    Some(any_type_ann())
152                });
153        }
154        has_expclicit_type
155    }
156
157    pub(crate) fn check_assign_pat_param(&mut self, assign_pat: &AssignPat) -> bool {
158        assign_pat
159            .left
160            .as_array()
161            .map(|array_pat| array_pat.type_ann.is_none())
162            .unwrap_or(false)
163            || assign_pat
164                .left
165                .as_object()
166                .map(|object_pat| object_pat.type_ann.is_none())
167                .unwrap_or(false)
168    }
169
170    pub(crate) fn add_undefined_type_for_param(&mut self, type_ann: &mut TsTypeAnn) -> bool {
171        if type_ann.type_ann.is_ts_type_ref() {
172            return true;
173        }
174
175        if !is_maybe_undefined(&type_ann.type_ann) {
176            let mut ty = type_ann.type_ann.clone();
177            if ty.is_ts_fn_or_constructor_type() {
178                ty = Box::new(
179                    TsParenthesizedType {
180                        span: DUMMY_SP,
181                        type_ann: Box::new(*ty),
182                    }
183                    .into(),
184                );
185            }
186            type_ann.type_ann = Box::new(TsType::TsUnionOrIntersectionType(
187                TsUnionOrIntersectionType::TsUnionType(TsUnionType {
188                    span: DUMMY_SP,
189                    types: vec![ty, ts_keyword_type(TsKeywordTypeKind::TsUndefinedKeyword)],
190                }),
191            ))
192        }
193
194        false
195    }
196
197    pub(crate) fn remove_function_overloads_in_module(items: &mut Vec<ModuleItem>) {
198        let mut last_function_name: Option<Atom> = None;
199        let mut is_export_default_function_overloads = false;
200
201        items.retain(|item| match item {
202            ModuleItem::Stmt(Stmt::Decl(Decl::Fn(fn_decl)))
203            | ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
204                decl: Decl::Fn(fn_decl),
205                ..
206            })) => {
207                if fn_decl.function.body.is_some() {
208                    if last_function_name
209                        .as_ref()
210                        .is_some_and(|last_name| last_name == &fn_decl.ident.sym)
211                    {
212                        return false;
213                    }
214                } else {
215                    last_function_name = Some(fn_decl.ident.sym.clone());
216                }
217                true
218            }
219            ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(export)) => {
220                if let Some(fn_expr) = export.decl.as_fn_expr() {
221                    if is_export_default_function_overloads && fn_expr.function.body.is_some() {
222                        is_export_default_function_overloads = false;
223                        false
224                    } else {
225                        is_export_default_function_overloads = true;
226                        true
227                    }
228                } else {
229                    is_export_default_function_overloads = false;
230                    true
231                }
232            }
233            _ => true,
234        });
235    }
236
237    pub(crate) fn remove_function_overloads_in_script(script: &mut Script) {
238        let mut last_function_name: Option<Atom> = None;
239        script.body.retain(|stmt| {
240            if let Some(fn_decl) = stmt.as_decl().and_then(|decl| decl.as_fn_decl()) {
241                if fn_decl.function.body.is_some() {
242                    if last_function_name
243                        .as_ref()
244                        .is_some_and(|last_name| last_name == &fn_decl.ident.sym)
245                    {
246                        return false;
247                    }
248                } else {
249                    last_function_name = Some(fn_decl.ident.sym.clone());
250                }
251            }
252            true
253        });
254    }
255}
256
257fn is_maybe_undefined(ts_type: &TsType) -> bool {
258    match ts_type {
259        TsType::TsKeywordType(keyword_type) => matches!(
260            keyword_type.kind,
261            TsKeywordTypeKind::TsAnyKeyword
262                | TsKeywordTypeKind::TsUndefinedKeyword
263                | TsKeywordTypeKind::TsUnknownKeyword
264        ),
265        TsType::TsUnionOrIntersectionType(TsUnionOrIntersectionType::TsUnionType(union_type)) => {
266            union_type.types.iter().any(|ty| is_maybe_undefined(ty))
267        }
268        _ => false,
269    }
270}