swc_ecma_transforms_typescript/
strip_type.rs

1use std::mem;
2
3use swc_common::util::take::Take;
4use swc_ecma_ast::*;
5use swc_ecma_utils::stack_size::maybe_grow_default;
6use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
7
8use crate::type_to_none;
9
10pub fn strip_type() -> impl VisitMut {
11    StripType::default()
12}
13
14/// This Module will strip all types/generics/interface/declares
15/// and type import/export.
16///
17/// `export declare var` in a namespace will be retained.
18#[derive(Default)]
19pub(crate) struct StripType {
20    in_namespace: bool,
21}
22
23impl VisitMut for StripType {
24    noop_visit_mut_type!(fail);
25
26    type_to_none!(visit_mut_opt_ts_type, Box<TsType>);
27
28    type_to_none!(visit_mut_opt_ts_type_ann, Box<TsTypeAnn>);
29
30    type_to_none!(visit_mut_opt_ts_type_param_decl, Box<TsTypeParamDecl>);
31
32    type_to_none!(
33        visit_mut_opt_ts_type_param_instantiation,
34        Box<TsTypeParamInstantiation>
35    );
36
37    fn visit_mut_array_pat(&mut self, n: &mut ArrayPat) {
38        n.visit_mut_children_with(self);
39        n.optional = false;
40    }
41
42    fn visit_mut_auto_accessor(&mut self, n: &mut AutoAccessor) {
43        n.type_ann = None;
44        n.accessibility = None;
45        n.definite = false;
46        n.is_override = false;
47        n.is_abstract = false;
48        n.visit_mut_children_with(self);
49    }
50
51    fn visit_mut_class(&mut self, n: &mut Class) {
52        n.is_abstract = false;
53        n.implements.clear();
54        n.visit_mut_children_with(self);
55    }
56
57    fn visit_mut_class_members(&mut self, n: &mut Vec<ClassMember>) {
58        n.retain(|member| match member {
59            ClassMember::TsIndexSignature(..) => false,
60            ClassMember::Constructor(Constructor { body: None, .. }) => false,
61
62            ClassMember::Method(ClassMethod {
63                is_abstract,
64                function,
65                ..
66            })
67            | ClassMember::PrivateMethod(PrivateMethod {
68                is_abstract,
69                function,
70                ..
71            }) => !is_abstract && function.body.is_some(),
72
73            ClassMember::ClassProp(
74                ClassProp { declare: true, .. }
75                | ClassProp {
76                    is_abstract: true, ..
77                },
78            )
79            | ClassMember::AutoAccessor(AutoAccessor {
80                is_abstract: true, ..
81            }) => false,
82
83            _ => true,
84        });
85
86        n.visit_mut_children_with(self);
87    }
88
89    fn visit_mut_class_method(&mut self, n: &mut ClassMethod) {
90        n.accessibility = None;
91        n.is_override = false;
92        n.is_abstract = false;
93        n.is_optional = false;
94        n.visit_mut_children_with(self);
95    }
96
97    fn visit_mut_class_prop(&mut self, prop: &mut ClassProp) {
98        prop.declare = false;
99        prop.readonly = false;
100        prop.is_override = false;
101        prop.is_optional = false;
102        prop.is_abstract = false;
103        prop.definite = false;
104        prop.accessibility = None;
105        prop.visit_mut_children_with(self);
106    }
107
108    fn visit_mut_private_method(&mut self, n: &mut PrivateMethod) {
109        n.accessibility = None;
110        n.is_abstract = false;
111        n.is_optional = false;
112        n.is_override = false;
113        n.visit_mut_children_with(self);
114    }
115
116    fn visit_mut_constructor(&mut self, n: &mut Constructor) {
117        n.accessibility = None;
118        n.visit_mut_children_with(self);
119    }
120
121    fn visit_mut_export_specifiers(&mut self, n: &mut Vec<ExportSpecifier>) {
122        n.retain(|s| match s {
123            ExportSpecifier::Named(ExportNamedSpecifier { is_type_only, .. }) => !is_type_only,
124            _ => true,
125        })
126    }
127
128    fn visit_mut_expr(&mut self, n: &mut Expr) {
129        // https://github.com/tc39/proposal-type-annotations#type-assertions
130        // https://github.com/tc39/proposal-type-annotations#non-nullable-assertions
131        while let Expr::TsAs(TsAsExpr { expr, .. })
132        | Expr::TsNonNull(TsNonNullExpr { expr, .. })
133        | Expr::TsTypeAssertion(TsTypeAssertion { expr, .. })
134        | Expr::TsConstAssertion(TsConstAssertion { expr, .. })
135        | Expr::TsInstantiation(TsInstantiation { expr, .. })
136        | Expr::TsSatisfies(TsSatisfiesExpr { expr, .. }) = n
137        {
138            *n = *expr.take();
139        }
140
141        maybe_grow_default(|| n.visit_mut_children_with(self));
142    }
143
144    // https://github.com/tc39/proposal-type-annotations#parameter-optionality
145    fn visit_mut_ident(&mut self, n: &mut Ident) {
146        n.optional = false;
147    }
148
149    fn visit_mut_import_specifiers(&mut self, n: &mut Vec<ImportSpecifier>) {
150        n.retain(|s| !matches!(s, ImportSpecifier::Named(named) if named.is_type_only));
151    }
152
153    fn visit_mut_ts_module_block(&mut self, node: &mut TsModuleBlock) {
154        let in_namespace = mem::replace(&mut self.in_namespace, true);
155        node.visit_mut_children_with(self);
156        self.in_namespace = in_namespace;
157    }
158
159    fn visit_mut_module_items(&mut self, n: &mut Vec<ModuleItem>) {
160        n.retain(|item| should_retain_module_item(item, self.in_namespace));
161        n.visit_mut_children_with(self);
162    }
163
164    fn visit_mut_object_pat(&mut self, pat: &mut ObjectPat) {
165        pat.visit_mut_children_with(self);
166        pat.optional = false;
167    }
168
169    // https://github.com/tc39/proposal-type-annotations#this-parameters
170    fn visit_mut_params(&mut self, n: &mut Vec<Param>) {
171        if n.first()
172            .filter(|param| {
173                matches!(
174                    &param.pat,
175                    Pat::Ident(BindingIdent {
176                        id: Ident { sym, .. },
177                        ..
178                    }) if &**sym == "this"
179                )
180            })
181            .is_some()
182        {
183            n.drain(0..1);
184        }
185
186        n.visit_mut_children_with(self);
187    }
188
189    fn visit_mut_private_prop(&mut self, prop: &mut PrivateProp) {
190        prop.readonly = false;
191        prop.is_override = false;
192        prop.is_optional = false;
193        prop.definite = false;
194        prop.accessibility = None;
195        prop.visit_mut_children_with(self);
196    }
197
198    fn visit_mut_setter_prop(&mut self, n: &mut SetterProp) {
199        n.this_param = None;
200
201        n.visit_mut_children_with(self);
202    }
203
204    fn visit_mut_simple_assign_target(&mut self, n: &mut SimpleAssignTarget) {
205        // https://github.com/tc39/proposal-type-annotations#type-assertions
206        // https://github.com/tc39/proposal-type-annotations#non-nullable-assertions
207        while let SimpleAssignTarget::TsAs(TsAsExpr { expr, .. })
208        | SimpleAssignTarget::TsNonNull(TsNonNullExpr { expr, .. })
209        | SimpleAssignTarget::TsTypeAssertion(TsTypeAssertion { expr, .. })
210        | SimpleAssignTarget::TsInstantiation(TsInstantiation { expr, .. })
211        | SimpleAssignTarget::TsSatisfies(TsSatisfiesExpr { expr, .. }) = n
212        {
213            *n = expr.take().try_into().unwrap();
214        }
215
216        n.visit_mut_children_with(self);
217    }
218
219    fn visit_mut_stmts(&mut self, n: &mut Vec<Stmt>) {
220        n.visit_mut_children_with(self);
221        n.retain(|s| !matches!(s, Stmt::Empty(e) if e.span.is_dummy()));
222    }
223
224    fn visit_mut_stmt(&mut self, n: &mut Stmt) {
225        if should_retain_stmt(n) {
226            n.visit_mut_children_with(self);
227        } else if !n.is_empty() {
228            n.take();
229        }
230    }
231
232    fn visit_mut_ts_import_equals_decl(&mut self, _: &mut TsImportEqualsDecl) {
233        // n.id.visit_mut_with(self);
234    }
235
236    fn visit_mut_ts_param_prop(&mut self, n: &mut TsParamProp) {
237        // skip accessibility
238        n.decorators.visit_mut_with(self);
239        n.param.visit_mut_with(self);
240    }
241}
242
243fn should_retain_module_item(module_item: &ModuleItem, in_namespace: bool) -> bool {
244    match module_item {
245        ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(export_decl)) => {
246            // https://github.com/swc-project/swc/issues/9821
247            // `export declare var` in namespace should be retained
248            // This will help the following transforms to work correctly
249            if in_namespace && export_decl.decl.is_var() {
250                return true;
251            }
252
253            should_retain_decl(&export_decl.decl)
254        }
255        ModuleItem::Stmt(stmt) => should_retain_stmt(stmt),
256        _ => module_item.is_concrete(),
257    }
258}
259
260fn should_retain_stmt(stmt: &Stmt) -> bool {
261    match stmt {
262        Stmt::Decl(decl) => should_retain_decl(decl),
263        _ => stmt.is_concrete(),
264    }
265}
266
267fn should_retain_decl(decl: &Decl) -> bool {
268    if decl.is_declare() {
269        return false;
270    }
271
272    decl.is_concrete()
273}
274
275pub(crate) trait IsConcrete {
276    fn is_concrete(&self) -> bool;
277}
278
279impl IsConcrete for TsModuleDecl {
280    fn is_concrete(&self) -> bool {
281        self.body
282            .as_ref()
283            .map(|body| body.is_concrete())
284            .unwrap_or_default()
285    }
286}
287
288impl IsConcrete for TsNamespaceBody {
289    fn is_concrete(&self) -> bool {
290        match self {
291            Self::TsModuleBlock(ts_module_block) => {
292                ts_module_block.body.iter().any(|item| item.is_concrete())
293            }
294            Self::TsNamespaceDecl(ts_namespace_decl) => ts_namespace_decl.body.is_concrete(),
295        }
296    }
297}
298
299impl IsConcrete for ModuleItem {
300    fn is_concrete(&self) -> bool {
301        match self {
302            Self::ModuleDecl(module_decl) => module_decl.is_concrete(),
303            Self::Stmt(stmt) => stmt.is_concrete(),
304        }
305    }
306}
307
308impl IsConcrete for ModuleDecl {
309    fn is_concrete(&self) -> bool {
310        match self {
311            Self::Import(import_decl) => !import_decl.type_only,
312            Self::ExportDecl(export_decl) => export_decl.decl.is_concrete(),
313            Self::ExportNamed(named_export) => !named_export.type_only,
314            Self::ExportDefaultDecl(export_default_decl) => export_default_decl.decl.is_concrete(),
315            Self::ExportDefaultExpr(..) => true,
316            Self::ExportAll(export_all) => !export_all.type_only,
317            Self::TsImportEquals(ts_import_equals) => !ts_import_equals.is_type_only,
318            Self::TsExportAssignment(..) => true,
319            Self::TsNamespaceExport(..) => false,
320        }
321    }
322}
323
324impl IsConcrete for Decl {
325    fn is_concrete(&self) -> bool {
326        match self {
327            Self::TsInterface(..) | Self::TsTypeAlias(..) => false,
328            Self::Fn(r#fn) => r#fn.function.body.is_some(),
329            Self::Class(..) | Self::Var(..) | Self::Using(..) | Self::TsEnum(..) => true,
330            Self::TsModule(ts_module) => ts_module.is_concrete(),
331        }
332    }
333}
334
335impl IsConcrete for DefaultDecl {
336    fn is_concrete(&self) -> bool {
337        match self {
338            Self::Class(_) => true,
339            Self::Fn(r#fn) => r#fn.function.body.is_some(),
340            Self::TsInterfaceDecl(..) => false,
341        }
342    }
343}
344
345impl IsConcrete for Stmt {
346    fn is_concrete(&self) -> bool {
347        match self {
348            Self::Empty(..) => false,
349            Self::Decl(decl) => decl.is_concrete(),
350            _ => true,
351        }
352    }
353}
354
355trait IsDeclare {
356    fn is_declare(&self) -> bool;
357}
358
359impl IsDeclare for Decl {
360    fn is_declare(&self) -> bool {
361        match self {
362            Decl::Class(class) => class.declare,
363            Decl::Fn(r#fn) => r#fn.declare,
364            Decl::Var(var) => var.declare,
365            Decl::Using(_) => false,
366            // NOTE: TsInterface/TsTypeAlias is not relevant
367            Decl::TsInterface(_) | Decl::TsTypeAlias(_) => true,
368            Decl::TsEnum(ts_enum) => ts_enum.declare,
369            Decl::TsModule(ts_module) => ts_module.declare || ts_module.global,
370        }
371    }
372}