Skip to content
Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
class Test { constructor(private [foo] = [1]) {} }

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
class Test { constructor(private { bar } = { bar: 1 }) {} }

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
class Test { constructor(private ...baz) {} }

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions packages/ast-spec/src/parameter/TSParameterProperty/spec.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import type { AST_NODE_TYPES } from '../../ast-node-types';
import type { Accessibility } from '../../base/Accessibility';
import type { BaseNode } from '../../base/BaseNode';
import type { Identifier } from '../../expression/Identifier/spec';
import type { Decorator } from '../../special/Decorator/spec';
import type { BindingName } from '../../unions/BindingName';
import type { AssignmentPattern } from '../AssignmentPattern/spec';
import type { RestElement } from '../RestElement/spec';

type ParameterPropertyParameter =
| (AssignmentPattern & { left: Identifier })
| Identifier;

export interface TSParameterProperty extends BaseNode {
type: AST_NODE_TYPES.TSParameterProperty;
accessibility: Accessibility | undefined;
decorators: Decorator[];
override: boolean;
parameter: AssignmentPattern | BindingName | RestElement;
parameter: ParameterPropertyParameter;
readonly: boolean;
static: boolean;
}
2 changes: 2 additions & 0 deletions packages/ast-spec/tests/fixtures-with-differences-errors.shot

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -359,19 +359,10 @@ export default createRule<Options, MessageIds>({
node: TSESTree.TSParameterProperty,
): void {
const nodeType = 'parameter property';
// HAS to be an identifier or assignment or TSC will throw
if (
node.parameter.type !== AST_NODE_TYPES.Identifier &&
node.parameter.type !== AST_NODE_TYPES.AssignmentPattern
) {
return;
}

const nodeName =
node.parameter.type === AST_NODE_TYPES.Identifier
? node.parameter.name
: // has to be an Identifier or TSC will throw an error
(node.parameter.left as TSESTree.Identifier).name;
: (node.parameter.left as TSESTree.Identifier).name;

switch (paramPropCheck) {
case 'explicit': {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,7 @@ export default createRule({
((node.parameter.type === AST_NODE_TYPES.Identifier && // constructor (public foo) {}
node.parameter.name === name) ||
(node.parameter.type === AST_NODE_TYPES.AssignmentPattern && // constructor (public foo = 1) {}
node.parameter.left.type === AST_NODE_TYPES.Identifier &&
node.parameter.left.name === name))
(node.parameter.left as TSESTree.Identifier).name === name))
);
}

Expand Down
11 changes: 1 addition & 10 deletions packages/eslint-plugin/src/rules/parameter-properties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,19 +107,10 @@ export default createRule<Options, MessageIds>({
const modifiers = getModifiers(node);

if (!allow.includes(modifiers)) {
// HAS to be an identifier or assignment or TSC will throw
if (
node.parameter.type !== AST_NODE_TYPES.Identifier &&
node.parameter.type !== AST_NODE_TYPES.AssignmentPattern
) {
return;
}

const name =
node.parameter.type === AST_NODE_TYPES.Identifier
? node.parameter.name
: // has to be an Identifier or TSC will throw an error
(node.parameter.left as TSESTree.Identifier).name;
: (node.parameter.left as TSESTree.Identifier).name;

context.report({
node,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,22 +59,11 @@ function extractNonComputedName(
*/
export function extractNameForMember(node: MemberNode): ExtractedName | null {
if (node.type === AST_NODE_TYPES.TSParameterProperty) {
switch (node.parameter.type) {
case AST_NODE_TYPES.ArrayPattern:
case AST_NODE_TYPES.ObjectPattern:
case AST_NODE_TYPES.RestElement:
// Nonsensical properties -- see https://github.com/typescript-eslint/typescript-eslint/issues/11708
return null;

case AST_NODE_TYPES.AssignmentPattern:
if (node.parameter.left.type !== AST_NODE_TYPES.Identifier) {
return null;
}
return extractNonComputedName(node.parameter.left);

case AST_NODE_TYPES.Identifier:
return extractNonComputedName(node.parameter);
}
const identifier =
node.parameter.type === AST_NODE_TYPES.Identifier
? node.parameter
: (node.parameter.left as TSESTree.Identifier);
return extractNonComputedName(identifier);
}

if (node.computed) {
Expand Down
10 changes: 3 additions & 7 deletions packages/eslint-plugin/src/util/collectUnusedVariables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,22 +141,18 @@ class UnusedVarsVisitor extends Visitor {
}

protected TSParameterProperty(node: TSESTree.TSParameterProperty): void {
let identifier: TSESTree.Identifier | null = null;
let identifier: TSESTree.Identifier;
switch (node.parameter.type) {
case AST_NODE_TYPES.AssignmentPattern:
if (node.parameter.left.type === AST_NODE_TYPES.Identifier) {
identifier = node.parameter.left;
}
identifier = node.parameter.left as TSESTree.Identifier;
break;

case AST_NODE_TYPES.Identifier:
identifier = node.parameter;
break;
}

if (identifier) {
this.markVariableAsUsed(identifier);
}
this.markVariableAsUsed(identifier);
}

private collectUnusedVariables(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,14 +302,6 @@ class Test {
},
{
code: `
class Test {
constructor(private { x }: any[]) {}
}
`,
options: [{ accessibility: 'no-public' }],
},
{
code: `
class Test {
#foo = 1;
#bar() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1382,27 +1382,16 @@ export class Test {
{
code: `
export class Test {
constructor(
public foo,
private ...bar,
) {}
constructor(public foo) {}
}
`,
errors: [
{
column: 12,
column: 22,
data: {
name: 'foo',
},
line: 4,
messageId: 'missingArgType',
},
{
column: 5,
data: {
name: 'bar',
},
line: 5,
line: 3,
messageId: 'missingArgType',
},
],
Expand Down
12 changes: 0 additions & 12 deletions packages/eslint-plugin/tests/rules/parameter-properties.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,18 +113,6 @@ class Foo {
`,
options: [{ allow: ['public readonly', 'private'] }],
},
// Semantically invalid test case
`
class Foo {
constructor(private ...name: string[]) {}
}
`,
// Semantically invalid test case
`
class Foo {
constructor(private [test]: [string]) {}
}
`,
{
code: `
class Foo {
Expand Down
18 changes: 18 additions & 0 deletions packages/typescript-estree/src/check-modifiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,24 @@ export function checkModifiers(node: ts.Node): void {
'A parameter property is only allowed in a constructor implementation.',
);
}
const param = node as ts.ParameterDeclaration;

if (param.dotDotDotToken) {
throwError(
modifier,
'A parameter property cannot be a rest parameter.',
);
}

if (
param.name.kind === SyntaxKind.ArrayBindingPattern ||
param.name.kind === SyntaxKind.ObjectBindingPattern
) {
throwError(
modifier,
'A parameter property may not be declared using a binding pattern.',
);
}
}

// There are more cases in `checkGrammarObjectLiteralExpression` in TypeScript.
Expand Down
2 changes: 1 addition & 1 deletion packages/typescript-estree/src/convert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1823,7 +1823,7 @@ export class Converter {
accessibility: getTSNodeAccessibility(node),
decorators: [],
override: hasModifier(SyntaxKind.OverrideKeyword, node),
parameter: result,
parameter: result as TSESTree.TSParameterProperty['parameter'],
readonly: hasModifier(SyntaxKind.ReadonlyKeyword, node),
static: hasModifier(SyntaxKind.StaticKeyword, node),
});
Expand Down