{"record":{"id":"5f4dc4f8953d6c08","repo":"swc-project/swc","slug":"tsparameterproperty-should-be-removed-by-typescrip","errorCode":null,"errorMessage":"TsParameterProperty should be removed by typescript::strip pass","messagePattern":"TsParameterProperty should be removed by typescript::strip pass","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/swc_ecma_compat_common/src/macros.rs","lineNumber":120,"sourceCode":"        }\n\n        fn visit_mut_constructor(&mut self, f: &mut Constructor) {\n            if f.body.is_none() {\n                return;\n            }\n\n            #[cfg(debug_assertions)]\n            tracing::trace!(\"visit_mut_constructor(parmas.len() = {})\", f.params.len());\n\n            f.visit_mut_children_with(self);\n\n            let mut params = f\n                .params\n                .take()\n                .into_iter()\n                .map(|pat| match pat {\n                    ParamOrTsParamProp::Param(p) => p,\n                    _ => unreachable!(\n                        \"TsParameterProperty should be removed by typescript::strip pass\"\n                    ),\n                })\n                .collect();\n\n            let body = f.body.as_mut().unwrap();\n            let (params, stmts) = self.visit_mut_fn_like(&mut params, &mut body.stmts);\n\n            #[cfg(debug_assertions)]\n            tracing::trace!(\n                \"visit_mut_constructor(parmas.len() = {}, after)\",\n                params.len()\n            );\n\n            f.params = params.into_iter().map(ParamOrTsParamProp::Param).collect();\n            body.stmts = stmts;\n        }\n    };","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/swc-project/swc/blob/5176682b65416c6b5de6b47379ae1588ea3ecb3f/crates/swc_ecma_compat_common/src/macros.rs#L102-L138","documentation":"The shared compat helper (behind the handle-functions macro used by es2015+ passes) rewrites function-like nodes and expects constructor parameters to already be plain Params. TypeScript parameter properties (`constructor(private x: number)`) are removed by the typescript strip pass; if it has not run, ParamOrTsParamProp::TsParamProp reaches unreachable!().","triggerScenarios":"Running an individual es2015 compat pass (classes, arrow, computed-props, etc. — anything built on these macros) directly on TypeScript source using constructor parameter properties, without typescript::strip having run first.","commonSituations":"Custom transform pipelines that cherry-pick compat passes instead of using swc's preset ordering; plugins applied to raw .ts input; migrating hand-rolled pass chains to newer swc versions.","solutions":["Use the official pass ordering: run the TypeScript transforms (typescript::strip or the full target chain via swc core's build/TransformOptions) before any es compat pass","If assembling passes manually, prepend swc_ecma_transforms_typescript::strip to the chain","Remove parameter properties from the source: declare fields explicitly and assign them in the constructor"],"exampleFix":"// before\nclass Service {\n  constructor(private logger: Logger) {}\n}\n\n// after\nclass Service {\n  private logger: Logger;\n  constructor(logger: Logger) { this.logger = logger; }\n}","handlingStrategy":"validation","validationCode":"// Detect TS constructor parameter properties before running raw compat passes\nlet param_prop = regex::Regex::new(\n    r#\"constructor\\s*\\([^)]*\\b(?:public|private|protected|readonly|override)\\s+[A-Za-z_$]\"#,\n).unwrap();\nif param_prop.is_match(&ts_source) {\n    return Err(\"run typescript::strip before es compat passes\".into());\n}","typeGuard":null,"tryCatchPattern":"if let Err(p) = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| apply_compat(&mut program))) {\n    if panic_message(&p).contains(\"TsParameterProperty\") {\n        // fix: prepend swc_ecma_transforms_typescript::strip to the chain and retry\n    } else { std::panic::resume_unwind(p); }\n}","preventionTips":["Use swc's official preset ordering (typescript strip before es compat) instead of hand-built chains","In manual chains, always prepend typescript::strip when input can be TypeScript","Lint against parameter properties in code processed by partial pipelines"],"tags":["swc","compat","typescript","pass-order","parameter-properties"],"backgroundTag":"typescript-strip-pass-order","analyzedSha":"5176682b65416c6b5de6b47379ae1588ea3ecb3f","analyzedAt":"2026-08-17T16:16:52.067Z","contentChangedAt":"2026-08-17T16:16:52.067Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}