Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/ts2371-declare-object-defaults.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
swc_core: patch
swc_ecma_parser: patch
---

fix(es/parser): emit TS2371 for object-pattern defaults in declare signatures
34 changes: 10 additions & 24 deletions crates/swc_ecma_parser/src/parser/class_and_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,16 +355,7 @@ impl<I: Tokens> Parser<I> {
if p.syntax().typescript() && body.is_none() {
// Declare functions cannot have assignment pattern in parameters
for param in &params {
// TODO: Search deeply for assignment pattern using a Visitor

let span = match &param.pat {
Pat::Assign(ref p) => Some(p.span()),
_ => None,
};

if let Some(span) = span {
p.emit_err(span, SyntaxError::TS2371)
}
p.emit_ts2371_for_param_initializers(&param.pat);
}
}

Expand Down Expand Up @@ -1249,22 +1240,17 @@ impl<I: Tokens> Parser<I> {
if self.syntax().typescript() && body.is_none() {
// Declare constructors cannot have assignment pattern in parameters
for param in &params {
// TODO: Search deeply for assignment pattern using a Visitor

let span = match *param {
ParamOrTsParamProp::Param(ref param) => match param.pat {
Pat::Assign(ref p) => Some(p.span()),
_ => None,
},
match param {
ParamOrTsParamProp::Param(param) => {
self.emit_ts2371_for_param_initializers(&param.pat);
}
ParamOrTsParamProp::TsParamProp(TsParamProp {
param: TsParamPropParam::Assign(ref p),
param: TsParamPropParam::Assign(p),
..
}) => Some(p.span()),
_ => None,
};

if let Some(span) = span {
self.emit_err(span, SyntaxError::TS2371)
}) => {
self.emit_err(p.span(), SyntaxError::TS2371);
}
_ => {}
}
}
}
Expand Down
9 changes: 8 additions & 1 deletion crates/swc_ecma_parser/src/parser/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,14 @@ impl<I: Tokens> Parser<I> {
};

let value = if self.input_mut().eat(Token::Eq) {
self.allow_in_expr(Self::parse_assignment_expr).map(Some)?
let right = self.allow_in_expr(Self::parse_assignment_expr)?;
// Ambient / declare signatures cannot have parameter initializers
// (TS2371). Object shorthand defaults use AssignPatProp and would
// otherwise skip the InDeclare check used for AssignPat.
if self.ctx().contains(Context::InDeclare) {
self.emit_err(self.span(start), SyntaxError::TS2371);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don't report TS2371 for ambient destructuring bindings

This check runs for every object binding pattern parsed under Context::InDeclare, not just function/constructor parameters. For example, declare const { a = 1 }: { a?: number }; reaches this branch while parsing the variable declarator, and TypeScript accepts that ambient destructuring binding, but SWC will now emit the parameter-only TS2371 diagnostic. Narrow the eager check to parameter/signature parsing (or rely on the post-parse parameter traversal) so declare variable bindings are not rejected.

Useful? React with 👍 / 👎.

Some(right)
} else {
let ctx = self.ctx();
if self.ctx().is_reserved_word(&key.sym) {
Expand Down
43 changes: 43 additions & 0 deletions crates/swc_ecma_parser/src/parser/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,49 @@ impl<I: Tokens> Parser<I> {
}
}

/// Emit TS2371 for any parameter initializer nested in a binding pattern.
///
/// Used for ambient / declare function and constructor signatures where
/// top-level AssignPat checks alone miss object shorthand defaults and
/// nested defaults.
pub(crate) fn emit_ts2371_for_param_initializers(&mut self, pat: &Pat) {
match pat {
Pat::Assign(a) => {
self.emit_err(a.span(), SyntaxError::TS2371);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid re-emitting TS2371 for parsed declare defaults

For declare function/method signatures parsed under Context::InParameters, nested Pat::Assign defaults have already reported TS2371 from parse_binding_element before this post-parse walk runs. This line reports the same span a second time for cases such as declare function f([x = 1]): void; or declare function f({ a: b = 1 }): void;, so consumers and the newly added stderr fixtures will see duplicate TS2371 diagnostics unless the walker skips already-checked assign patterns in that context or the eager check is centralized here.

Useful? React with 👍 / 👎.

self.emit_ts2371_for_param_initializers(&a.left);
}
Pat::Array(arr) => {
for elem in arr.elems.iter().flatten() {
self.emit_ts2371_for_param_initializers(elem);
}
}
Pat::Object(obj) => {
for prop in &obj.props {
match prop {
ObjectPatProp::KeyValue(KeyValuePatProp { value, .. })
| ObjectPatProp::Rest(RestPat { arg: value, .. }) => {
self.emit_ts2371_for_param_initializers(value);
}
ObjectPatProp::Assign(AssignPatProp {
span,
value: Some(_),
..
}) => {
self.emit_err(*span, SyntaxError::TS2371);
}
ObjectPatProp::Assign(AssignPatProp { value: None, .. }) => {}
#[cfg(swc_ast_unknown)]
_ => {}
}
}
}
Pat::Rest(r) => self.emit_ts2371_for_param_initializers(&r.arg),
Pat::Ident(_) | Pat::Invalid(_) | Pat::Expr(_) => {}
#[cfg(swc_ast_unknown)]
_ => {}
}
}

fn assign_pat_type_ann(&mut self, pat: &mut Pat, span: Span, type_ann: Box<TsType>) {
let type_ann = Some(Box::new(TsTypeAnn { span, type_ann }));

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
declare function bar([x = 1]: number[]): void;
declare function baz([x, y = 2]: number[]): void;
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
x A parameter initializer is only allowed in a function or constructor implementation
,-[$DIR/tests/typescript-errors/ts2371/array-still-errors/input.ts:1:1]
1 | declare function bar([x = 1]: number[]): void;
: ^^^^^
2 | declare function baz([x, y = 2]: number[]): void;
`----
x A parameter initializer is only allowed in a function or constructor implementation
,-[$DIR/tests/typescript-errors/ts2371/array-still-errors/input.ts:2:1]
1 | declare function bar([x = 1]: number[]): void;
2 | declare function baz([x, y = 2]: number[]): void;
: ^^^^^
`----
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
declare class C {
method({ a = 1 }: { a?: number }): void;
constructor({ a = 1 }: { a?: number });
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
x A parameter initializer is only allowed in a function or constructor implementation
,-[$DIR/tests/typescript-errors/ts2371/declare-method/input.ts:2:1]
1 | declare class C {
2 | method({ a = 1 }: { a?: number }): void;
: ^^^^^
3 | constructor({ a = 1 }: { a?: number });
`----
x A parameter initializer is only allowed in a function or constructor implementation
,-[$DIR/tests/typescript-errors/ts2371/declare-method/input.ts:3:1]
2 | method({ a = 1 }: { a?: number }): void;
3 | constructor({ a = 1 }: { a?: number });
: ^^^^^
4 | }
`----
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
declare function foo({ a: b = 1 }: { a?: number }): void;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
x A parameter initializer is only allowed in a function or constructor implementation
,-[$DIR/tests/typescript-errors/ts2371/key-value-default/input.ts:1:1]
1 | declare function foo({ a: b = 1 }: { a?: number }): void;
: ^^^^^
`----
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
declare function top(a = 1): void;
declare function obj({ a = 1 }): void;
declare function arr([a = 1]): void;
declare function nested({ a: { b = 1 } }): void;
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
x A parameter initializer is only allowed in a function or constructor implementation
,-[$DIR/tests/typescript-errors/ts2371/mixed/input.ts:1:1]
1 | declare function top(a = 1): void;
: ^^^^^
2 | declare function obj({ a = 1 }): void;
`----
x A parameter initializer is only allowed in a function or constructor implementation
,-[$DIR/tests/typescript-errors/ts2371/mixed/input.ts:2:1]
1 | declare function top(a = 1): void;
2 | declare function obj({ a = 1 }): void;
: ^^^^^
3 | declare function arr([a = 1]): void;
`----
x A parameter initializer is only allowed in a function or constructor implementation
,-[$DIR/tests/typescript-errors/ts2371/mixed/input.ts:3:1]
2 | declare function obj({ a = 1 }): void;
3 | declare function arr([a = 1]): void;
: ^^^^^
4 | declare function nested({ a: { b = 1 } }): void;
`----
x A parameter initializer is only allowed in a function or constructor implementation
,-[$DIR/tests/typescript-errors/ts2371/mixed/input.ts:4:1]
3 | declare function arr([a = 1]): void;
4 | declare function nested({ a: { b = 1 } }): void;
: ^^^^^
`----
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
declare function foo({ a: { b = 1 } }: { a: { b?: number } }): void;
declare function bar({ a: [x = 1] }: { a: number[] }): void;
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
x A parameter initializer is only allowed in a function or constructor implementation
,-[$DIR/tests/typescript-errors/ts2371/nested-object/input.ts:1:1]
1 | declare function foo({ a: { b = 1 } }: { a: { b?: number } }): void;
: ^^^^^
2 | declare function bar({ a: [x = 1] }: { a: number[] }): void;
`----
x A parameter initializer is only allowed in a function or constructor implementation
,-[$DIR/tests/typescript-errors/ts2371/nested-object/input.ts:2:1]
1 | declare function foo({ a: { b = 1 } }: { a: { b?: number } }): void;
2 | declare function bar({ a: [x = 1] }: { a: number[] }): void;
: ^^^^^
`----
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
declare function foo({ a = 1 }: { a?: number }): void;
declare function bar({ a = 1, b = 2 }: { a?: number; b?: number }): void;
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
x A parameter initializer is only allowed in a function or constructor implementation
,-[$DIR/tests/typescript-errors/ts2371/object-shorthand/input.ts:1:1]
1 | declare function foo({ a = 1 }: { a?: number }): void;
: ^^^^^
2 | declare function bar({ a = 1, b = 2 }: { a?: number; b?: number }): void;
`----
x A parameter initializer is only allowed in a function or constructor implementation
,-[$DIR/tests/typescript-errors/ts2371/object-shorthand/input.ts:2:1]
1 | declare function foo({ a = 1 }: { a?: number }): void;
2 | declare function bar({ a = 1, b = 2 }: { a?: number; b?: number }): void;
: ^^^^^
`----
x A parameter initializer is only allowed in a function or constructor implementation
,-[$DIR/tests/typescript-errors/ts2371/object-shorthand/input.ts:2:1]
1 | declare function foo({ a = 1 }: { a?: number }): void;
2 | declare function bar({ a = 1, b = 2 }: { a?: number; b?: number }): void;
: ^^^^^
`----
Loading