diff --git a/crates/oxc_ast/src/ast/ts.rs b/crates/oxc_ast/src/ast/ts.rs index 494b75c98..9dc5b6c5a 100644 --- a/crates/oxc_ast/src/ast/ts.rs +++ b/crates/oxc_ast/src/ast/ts.rs @@ -118,6 +118,7 @@ pub enum TSType<'a> { TSIntersectionType(Box<'a, TSIntersectionType<'a>>), TSLiteralType(Box<'a, TSLiteralType<'a>>), TSMappedType(Box<'a, TSMappedType<'a>>), + TSNamedTupleMember(Box<'a, TSNamedTupleMember<'a>>), TSQualifiedName(Box<'a, TSQualifiedName<'a>>), TSTemplateLiteralType(Box<'a, TSTemplateLiteralType<'a>>), TSTupleType(Box<'a, TSTupleType<'a>>), diff --git a/crates/oxc_ast/src/span.rs b/crates/oxc_ast/src/span.rs index 8ca48f9b8..99187f9a1 100644 --- a/crates/oxc_ast/src/span.rs +++ b/crates/oxc_ast/src/span.rs @@ -323,6 +323,7 @@ impl<'a> GetSpan for TSType<'a> { Self::TSArrayType(t) => t.span, Self::TSIntersectionType(t) => t.span, Self::TSMappedType(t) => t.span, + Self::TSNamedTupleMember(t) => t.span, Self::TSInferType(t) => t.span, Self::TSConstructorType(t) => t.span, Self::TSIndexedAccessType(t) => t.span, diff --git a/crates/oxc_codegen/src/gen_ts.rs b/crates/oxc_codegen/src/gen_ts.rs index 8f73dda8a..c6dc04fbf 100644 --- a/crates/oxc_codegen/src/gen_ts.rs +++ b/crates/oxc_codegen/src/gen_ts.rs @@ -139,6 +139,9 @@ impl<'a, const MINIFY: bool> Gen for TSType<'a> { p.print_semicolon_if_needed(); p.print_str(b"}"); } + Self::TSNamedTupleMember(decl) => { + decl.gen(p, ctx); + } Self::TSLiteralType(decl) => { decl.literal.gen(p, ctx); } @@ -507,15 +510,21 @@ impl<'a, const MINIFY: bool> Gen for TSTupleElement<'a> { ts_type.type_annotation.gen(p, ctx); } TSTupleElement::TSNamedTupleMember(ts_type) => { - ts_type.label.gen(p, ctx); - p.print_str(b":"); - p.print_soft_space(); - ts_type.element_type.gen(p, ctx); + ts_type.gen(p, ctx); } } } } +impl<'a, const MINIFY: bool> Gen for TSNamedTupleMember<'a> { + fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { + self.label.gen(p, ctx); + p.print_str(b":"); + p.print_soft_space(); + self.element_type.gen(p, ctx); + } +} + impl<'a, const MINIFY: bool> Gen for TSModuleDeclaration<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { if self.modifiers.contains(ModifierKind::Export) { diff --git a/crates/oxc_linter/src/rules/typescript/array_type.rs b/crates/oxc_linter/src/rules/typescript/array_type.rs index 32a3263d7..57ab112e0 100644 --- a/crates/oxc_linter/src/rules/typescript/array_type.rs +++ b/crates/oxc_linter/src/rules/typescript/array_type.rs @@ -408,6 +408,7 @@ fn get_ts_element_type_span(ts_type: &TSType) -> Option { TSType::TSIntersectionType(t) => Some(t.span), TSType::TSLiteralType(t) => Some(t.span), TSType::TSMappedType(t) => Some(t.span), + TSType::TSNamedTupleMember(t) => Some(t.span), TSType::TSQualifiedName(t) => Some(t.span), TSType::TSTemplateLiteralType(t) => Some(t.span), TSType::TSTupleType(t) => Some(t.span), diff --git a/crates/oxc_parser/src/ts/list.rs b/crates/oxc_parser/src/ts/list.rs index 433df24f0..2452d8732 100644 --- a/crates/oxc_parser/src/ts/list.rs +++ b/crates/oxc_parser/src/ts/list.rs @@ -52,7 +52,23 @@ impl<'a> SeparatedList<'a> for TSTupleElementList<'a> { fn parse_element(&mut self, p: &mut ParserImpl<'a>) -> Result<()> { let span = p.start_span(); if p.is_at_named_tuple_element() { - let _is_rest = p.eat(Kind::Dot3); + if p.eat(Kind::Dot3) { + let member_span = p.start_span(); + let label = p.parse_identifier_name()?; + p.expect(Kind::Colon)?; + let element_type = p.parse_ts_type()?; + self.elements.push(TSTupleElement::TSRestType(p.ast.alloc(TSRestType { + span: p.end_span(span), + type_annotation: TSType::TSNamedTupleMember(p.ast.alloc(TSNamedTupleMember { + span: p.end_span(member_span), + element_type, + label, + optional: false, // A tuple member cannot be both optional and rest. (TS5085) + })), + }))); + return Ok(()); + } + let label = p.parse_identifier_name()?; let optional = p.eat(Kind::Question); p.expect(Kind::Colon)?; diff --git a/crates/oxc_prettier/src/format/mod.rs b/crates/oxc_prettier/src/format/mod.rs index 9895aa14c..03b898b55 100644 --- a/crates/oxc_prettier/src/format/mod.rs +++ b/crates/oxc_prettier/src/format/mod.rs @@ -676,6 +676,7 @@ impl<'a> Format<'a> for TSType<'a> { TSType::TSIntersectionType(v) => v.format(p), TSType::TSLiteralType(v) => v.format(p), TSType::TSMappedType(v) => v.format(p), + TSType::TSNamedTupleMember(v) => v.format(p), TSType::TSQualifiedName(v) => v.format(p), TSType::TSTemplateLiteralType(v) => v.format(p), TSType::TSTupleType(v) => v.format(p), @@ -843,6 +844,12 @@ impl<'a> Format<'a> for TSMappedType<'a> { } } +impl<'a> Format<'a> for TSNamedTupleMember<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + line!() + } +} + impl<'a> Format<'a> for TSQualifiedName<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { line!()