diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index 0536b6087..6899c271b 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -1530,6 +1530,22 @@ pub struct Function<'a> { pub expression: bool, pub generator: bool, pub r#async: bool, + /// Declaring `this` in a Function + /// + /// The JavaScript specification states that you cannot have a parameter called `this`, + /// and so TypeScript uses that syntax space to let you declare the type for `this` in the function body. + /// + /// ```TypeScript + /// interface DB { + /// filterUsers(filter: (this: User) => boolean): User[]; + /// } + /// + /// const db = getDB(); + /// const admins = db.filterUsers(function (this: User) { + /// return this.admin; + /// }); + /// ``` + pub this_param: Option>, pub params: Box<'a, FormalParameters<'a>>, pub body: Option>>, pub type_parameters: Option>>, @@ -1590,10 +1606,6 @@ impl<'a> FormalParameters<'a> { pub fn parameters_count(&self) -> usize { self.items.len() + self.rest.as_ref().map_or(0, |_| 1) } - - pub fn this_parameter(&self) -> Option<&FormalParameter<'a>> { - self.items.first().filter(|item| matches!(&item.pattern.kind, BindingPatternKind::BindingIdentifier(ident) if ident.name == "this")) - } } #[derive(Debug, Hash)] diff --git a/crates/oxc_ast/src/ast/ts.rs b/crates/oxc_ast/src/ast/ts.rs index 14db099be..9ad24a25b 100644 --- a/crates/oxc_ast/src/ast/ts.rs +++ b/crates/oxc_ast/src/ast/ts.rs @@ -11,6 +11,15 @@ use serde::Serialize; #[allow(clippy::wildcard_imports)] use crate::ast::*; +#[derive(Debug, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))] +pub struct TSThisParameter<'a> { + #[cfg_attr(feature = "serde", serde(flatten))] + pub span: Span, + pub this: IdentifierName, + pub type_annotation: Option>>, +} + /// Enum Declaration /// /// `const_opt` enum `BindingIdentifier` { `EnumBody_opt` } @@ -548,6 +557,7 @@ pub struct TSIndexSignature<'a> { pub struct TSCallSignatureDeclaration<'a> { #[cfg_attr(feature = "serde", serde(flatten))] pub span: Span, + pub this_param: Option>, pub params: Box<'a, FormalParameters<'a>>, pub return_type: Option>>, pub type_parameters: Option>>, @@ -570,6 +580,7 @@ pub struct TSMethodSignature<'a> { pub computed: bool, pub optional: bool, pub kind: TSMethodSignatureKind, + pub this_param: Option>, pub params: Box<'a, FormalParameters<'a>>, pub return_type: Option>>, pub type_parameters: Option>>, @@ -703,6 +714,7 @@ pub struct TSImportType<'a> { pub struct TSFunctionType<'a> { #[cfg_attr(feature = "serde", serde(flatten))] pub span: Span, + pub this_param: Option>, pub params: Box<'a, FormalParameters<'a>>, pub return_type: Box<'a, TSTypeAnnotation<'a>>, pub type_parameters: Option>>, diff --git a/crates/oxc_ast/src/ast_builder.rs b/crates/oxc_ast/src/ast_builder.rs index 70a308d38..84ac29a13 100644 --- a/crates/oxc_ast/src/ast_builder.rs +++ b/crates/oxc_ast/src/ast_builder.rs @@ -742,6 +742,15 @@ impl<'a> AstBuilder<'a> { FormalParameter { span, pattern, accessibility, readonly, decorators } } + pub fn ts_this_parameter( + &self, + span: Span, + this: IdentifierName, + type_annotation: Option>>, + ) -> TSThisParameter<'a> { + TSThisParameter { span, this, type_annotation } + } + pub fn function( &self, r#type: FunctionType, @@ -750,6 +759,7 @@ impl<'a> AstBuilder<'a> { expression: bool, generator: bool, r#async: bool, + this_param: Option>, params: Box<'a, FormalParameters<'a>>, body: Option>>, type_parameters: Option>>, @@ -763,6 +773,7 @@ impl<'a> AstBuilder<'a> { expression, generator, r#async, + this_param, params, body, type_parameters, @@ -1307,12 +1318,14 @@ impl<'a> AstBuilder<'a> { pub fn ts_call_signature_declaration( &self, span: Span, + this_param: Option>, params: Box<'a, FormalParameters<'a>>, return_type: Option>>, type_parameters: Option>>, ) -> TSSignature<'a> { TSSignature::TSCallSignatureDeclaration(self.alloc(TSCallSignatureDeclaration { span, + this_param, params, return_type, type_parameters, @@ -1341,6 +1354,7 @@ impl<'a> AstBuilder<'a> { computed: bool, optional: bool, kind: TSMethodSignatureKind, + this_param: Option>, params: Box<'a, FormalParameters<'a>>, return_type: Option>>, type_parameters: Option>>, @@ -1351,6 +1365,7 @@ impl<'a> AstBuilder<'a> { computed, optional, kind, + this_param, params, return_type, type_parameters, @@ -1627,12 +1642,14 @@ impl<'a> AstBuilder<'a> { pub fn ts_function_type( &self, span: Span, + this_param: Option>, params: Box<'a, FormalParameters<'a>>, return_type: Box<'a, TSTypeAnnotation<'a>>, type_parameters: Option>>, ) -> TSType<'a> { TSType::TSFunctionType(self.alloc(TSFunctionType { span, + this_param, params, return_type, type_parameters, diff --git a/crates/oxc_parser/src/js/class.rs b/crates/oxc_parser/src/js/class.rs index 1e6a67602..039f59813 100644 --- a/crates/oxc_parser/src/js/class.rs +++ b/crates/oxc_parser/src/js/class.rs @@ -357,15 +357,11 @@ impl<'a> Parser<'a> { let value = self.parse_method(r#async, generator)?; - if kind == MethodDefinitionKind::Get - && value.params.parameters_count() != value.params.this_parameter().map_or(0, |_| 1) - { + if kind == MethodDefinitionKind::Get && value.params.parameters_count() != 0 { self.error(diagnostics::GetterParameters(value.params.span)); } - if kind == MethodDefinitionKind::Set - && value.params.parameters_count() != value.params.this_parameter().map_or(1, |_| 2) - { + if kind == MethodDefinitionKind::Set && value.params.parameters_count() != 1 { self.error(diagnostics::SetterParameters(value.params.span)); } diff --git a/crates/oxc_parser/src/js/function.rs b/crates/oxc_parser/src/js/function.rs index 0ccf5bbde..23dfbe4bf 100644 --- a/crates/oxc_parser/src/js/function.rs +++ b/crates/oxc_parser/src/js/function.rs @@ -74,6 +74,18 @@ impl<'a> Parser<'a> { Ok(self.ast.formal_parameters(self.end_span(span), params_kind, list.elements, list.rest)) } + pub(crate) fn parse_formal_parameters_with_ts_this( + &mut self, + params_kind: FormalParameterKind, + ) -> Result<(Option>, Box<'a, FormalParameters<'a>>)> { + let span = self.start_span(); + let list: FormalParameterList<'_> = FormalParameterList::parse(self)?; + let formal_parameters = + self.ast.formal_parameters(self.end_span(span), params_kind, list.elements, list.rest); + let this_param = list.this_param; + Ok((this_param, formal_parameters)) + } + pub(crate) fn parse_function( &mut self, span: Span, @@ -88,7 +100,8 @@ impl<'a> Parser<'a> { let type_parameters = self.parse_ts_type_parameters()?; - let params = self.parse_formal_parameters(FormalParameterKind::FormalParameter)?; + let (this_param, params) = + self.parse_formal_parameters_with_ts_this(FormalParameterKind::FormalParameter)?; let return_type = self.parse_ts_return_type_annotation()?; @@ -124,6 +137,7 @@ impl<'a> Parser<'a> { false, // expression generator, r#async, + this_param, params, body, type_parameters, diff --git a/crates/oxc_parser/src/js/list.rs b/crates/oxc_parser/src/js/list.rs index 10ca57e37..835156cb7 100644 --- a/crates/oxc_parser/src/js/list.rs +++ b/crates/oxc_parser/src/js/list.rs @@ -234,11 +234,12 @@ impl<'a> SeparatedList<'a> for SequenceExpressionList<'a> { pub struct FormalParameterList<'a> { pub elements: Vec<'a, FormalParameter<'a>>, pub rest: Option>>, + pub this_param: Option>, } impl<'a> SeparatedList<'a> for FormalParameterList<'a> { fn new(p: &Parser<'a>) -> Self { - Self { elements: p.ast.new_vec(), rest: None } + Self { elements: p.ast.new_vec(), rest: None, this_param: None } } fn open(&self) -> Kind { @@ -260,8 +261,8 @@ impl<'a> SeparatedList<'a> for FormalParameterList<'a> { match p.cur_kind() { Kind::This if p.ts_enabled() => { - let formal_parameter = p.parse_ts_this_parameter()?; - self.elements.push(formal_parameter); + let this_parameter = p.parse_ts_this_parameter()?; + self.this_param.replace(this_parameter); } Kind::Dot3 => { let rest = p.parse_rest_element()?; diff --git a/crates/oxc_parser/src/js/object.rs b/crates/oxc_parser/src/js/object.rs index 54ad6d4e2..cfe37a2a9 100644 --- a/crates/oxc_parser/src/js/object.rs +++ b/crates/oxc_parser/src/js/object.rs @@ -217,7 +217,7 @@ impl<'a> Parser<'a> { self.expect(Kind::Get)?; let (key, computed) = self.parse_property_name()?; let method = self.parse_method(false, false)?; - if method.params.parameters_count() != method.params.this_parameter().map_or(0, |_| 1) { + if method.params.parameters_count() != 0 { self.error(diagnostics::GetterParameters(method.params.span)); } let value = self.ast.function_expression(method); @@ -241,7 +241,7 @@ impl<'a> Parser<'a> { let (key, computed) = self.parse_property_name()?; let method = self.parse_method(false, false)?; - if method.params.parameters_count() != method.params.this_parameter().map_or(1, |_| 2) { + if method.params.parameters_count() != 1 { self.error(diagnostics::SetterParameters(method.params.span)); } diff --git a/crates/oxc_parser/src/ts/statement.rs b/crates/oxc_parser/src/ts/statement.rs index 01dc6f4ef..7cd0077c6 100644 --- a/crates/oxc_parser/src/ts/statement.rs +++ b/crates/oxc_parser/src/ts/statement.rs @@ -422,19 +422,16 @@ impl<'a> Parser<'a> { )) } - pub(crate) fn parse_ts_this_parameter(&mut self) -> Result> { + pub(crate) fn parse_ts_this_parameter(&mut self) -> Result> { let span = self.start_span(); - let (ident_span, name) = self.parse_identifier_kind(Kind::This); + + let this = { + let (span, name) = self.parse_identifier_kind(Kind::This); + IdentifierName { span, name } + }; + let type_annotation = self.parse_ts_type_annotation()?; - let kind = self.ast.binding_pattern_identifier(BindingIdentifier::new(ident_span, name)); - let binding = self.ast.binding_pattern(kind, type_annotation, /* optional */ false); - Ok(self.ast.formal_parameter( - self.end_span(span), - binding, - /* accessibility */ None, - /* readonly */ false, - /* decorators */ self.ast.new_vec(), - )) + Ok(self.ast.ts_this_parameter(self.end_span(span), this, type_annotation)) } pub(crate) fn eat_decorators(&mut self) -> Result<()> { diff --git a/crates/oxc_parser/src/ts/types.rs b/crates/oxc_parser/src/ts/types.rs index eef4f4210..8bd58bb64 100644 --- a/crates/oxc_parser/src/ts/types.rs +++ b/crates/oxc_parser/src/ts/types.rs @@ -802,12 +802,19 @@ impl<'a> Parser<'a> { fn parse_ts_function_type(&mut self) -> Result> { let span = self.start_span(); let type_parameters = self.parse_ts_type_parameters()?; - let params = self.parse_formal_parameters(FormalParameterKind::Signature)?; + let (this_param, params) = + self.parse_formal_parameters_with_ts_this(FormalParameterKind::Signature)?; self.expect(Kind::Arrow)?; let return_type_span = self.start_span(); let return_type = self.parse_ts_return_type()?; let return_type = self.ast.ts_type_annotation(self.end_span(return_type_span), return_type); - Ok(self.ast.ts_function_type(self.end_span(span), params, return_type, type_parameters)) + Ok(self.ast.ts_function_type( + self.end_span(span), + this_param, + params, + return_type, + type_parameters, + )) } fn parse_ts_infer_type(&mut self) -> Result> { @@ -913,12 +920,14 @@ impl<'a> Parser<'a> { pub(crate) fn parse_ts_call_signature_member(&mut self) -> Result> { let span = self.start_span(); let type_parameters = self.parse_ts_type_parameters()?; - let params = self.parse_formal_parameters(FormalParameterKind::Signature)?; + let (this_patam, params) = + self.parse_formal_parameters_with_ts_this(FormalParameterKind::Signature)?; let return_type = self.parse_ts_return_type_annotation()?; self.bump(Kind::Comma); self.bump(Kind::Semicolon); Ok(self.ast.ts_call_signature_declaration( self.end_span(span), + this_patam, params, return_type, type_parameters, @@ -929,7 +938,8 @@ impl<'a> Parser<'a> { let span = self.start_span(); self.expect(Kind::Get)?; let (key, computed) = self.parse_property_name()?; - let params = self.parse_formal_parameters(FormalParameterKind::Signature)?; + let (this_param, params) = + self.parse_formal_parameters_with_ts_this(FormalParameterKind::Signature)?; let return_type = self.parse_ts_return_type_annotation()?; self.bump(Kind::Comma); self.bump(Kind::Semicolon); @@ -939,6 +949,7 @@ impl<'a> Parser<'a> { computed, /* optional */ false, TSMethodSignatureKind::Get, + this_param, params, return_type, None, @@ -949,7 +960,8 @@ impl<'a> Parser<'a> { let span = self.start_span(); self.expect(Kind::Set)?; let (key, computed) = self.parse_property_name()?; - let params = self.parse_formal_parameters(FormalParameterKind::Signature)?; + let (this_param, params) = + self.parse_formal_parameters_with_ts_this(FormalParameterKind::Signature)?; let return_type = self.parse_ts_return_type_annotation()?; self.bump(Kind::Comma); self.bump(Kind::Semicolon); @@ -962,6 +974,7 @@ impl<'a> Parser<'a> { computed, /* optional */ false, TSMethodSignatureKind::Set, + this_param, params, return_type, None, @@ -996,6 +1009,7 @@ impl<'a> Parser<'a> { computed, optional, TSMethodSignatureKind::Method, + call_signature.this_param, call_signature.params, call_signature.return_type, call_signature.type_parameters, diff --git a/crates/oxc_transformer/src/es2015/arrow_functions.rs b/crates/oxc_transformer/src/es2015/arrow_functions.rs index d8b528f01..06817b50b 100644 --- a/crates/oxc_transformer/src/es2015/arrow_functions.rs +++ b/crates/oxc_transformer/src/es2015/arrow_functions.rs @@ -130,6 +130,7 @@ impl<'a> ArrowFunctions<'a> { false, arrow_expr.generator, arrow_expr.r#async, + None, self.ast.copy(&arrow_expr.params), Some(body), self.ast.copy(&arrow_expr.type_parameters), diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index 68d70fa84..df2f808e2 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -223,18 +223,6 @@ impl<'a> VisitMut<'a> for Transformer<'a> { }); } - fn visit_formal_parameters(&mut self, params: &mut FormalParameters<'a>) { - self.typescript.as_mut().map(|t| t.transform_formal_parameters(params)); - - for param in params.items.iter_mut() { - self.visit_formal_parameter(param); - } - - if let Some(rest) = &mut params.rest { - self.visit_rest_element(rest); - } - } - fn visit_variable_declarator(&mut self, declarator: &mut VariableDeclarator<'a>) { self.es2015_function_name.as_mut().map(|t| t.transform_variable_declarator(declarator)); diff --git a/crates/oxc_transformer/src/typescript/mod.rs b/crates/oxc_transformer/src/typescript/mod.rs index 10be153b2..71b3815a0 100644 --- a/crates/oxc_transformer/src/typescript/mod.rs +++ b/crates/oxc_transformer/src/typescript/mod.rs @@ -33,13 +33,6 @@ impl<'a> TypeScript<'a> { Self { ast, ctx, verbatim_module_syntax, export_name_set: FxHashSet::default() } } - #[allow(clippy::unused_self)] - pub fn transform_formal_parameters(&self, params: &mut FormalParameters<'a>) { - if params.items.get(0).is_some_and(|param| matches!(¶m.pattern.kind, BindingPatternKind::BindingIdentifier(ident) if ident.name =="this")) { - params.items.remove(0); - } - } - /// ```TypeScript /// enum Foo { /// X