eslint-plugin-no-excess-properties 0.0.7 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -14,7 +14,7 @@ declare const plugin: {
14
14
  version: string;
15
15
  };
16
16
  rules: {
17
- "object-literal": import("@typescript-eslint/utils/ts-eslint").RuleModule<"noExcessProperties", [], import("./object-literal").PluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
17
+ "object-literal": import("@typescript-eslint/utils/ts-eslint").RuleModule<"noExcessProperties" | "noExcessProperty", [], import("./object-literal").PluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
18
18
  };
19
19
  };
20
20
  export = plugin;
@@ -4,5 +4,5 @@ export interface PluginDocs {
4
4
  recommended?: boolean;
5
5
  requiresTypeChecking?: boolean;
6
6
  }
7
- declare const noExcessProperties: ESLintUtils.RuleModule<"noExcessProperties", [], PluginDocs, ESLintUtils.RuleListener>;
7
+ declare const noExcessProperties: ESLintUtils.RuleModule<"noExcessProperties" | "noExcessProperty", [], PluginDocs, ESLintUtils.RuleListener>;
8
8
  export default noExcessProperties;
@@ -44,56 +44,95 @@ function isObjectLiteral(type) {
44
44
  return (type.symbol !== undefined &&
45
45
  tsutils.isSymbolFlagSet(type.symbol, typescript_1.default.SymbolFlags.ObjectLiteral));
46
46
  }
47
- function resolveType(type) {
48
- let resolvedType = type;
49
- const callSignatures = resolvedType.getCallSignatures();
50
- if (callSignatures.length === 1) {
51
- resolvedType = callSignatures[0].getReturnType();
52
- }
53
- const arrayType = resolvedType.getNumberIndexType();
54
- if (arrayType) {
55
- resolvedType = arrayType;
47
+ function isChildNode(node, parentNode) {
48
+ let parent = node;
49
+ do {
50
+ parent = parent.parent;
51
+ if (parent === parentNode) {
52
+ return true;
53
+ }
54
+ } while (parent);
55
+ return false;
56
+ }
57
+ function splitTypes(types) {
58
+ const result = {
59
+ advancedTypes: [[], []],
60
+ basicTypes: [],
61
+ };
62
+ for (const type of types) {
63
+ const callSignatures = type.getCallSignatures();
64
+ if (callSignatures.length === 1) {
65
+ result.advancedTypes[0].push(callSignatures[0].getReturnType());
66
+ continue;
67
+ }
68
+ const arrayType = type.getNumberIndexType();
69
+ if (arrayType && type.symbol?.name === "Array") {
70
+ result.advancedTypes[1].push(arrayType);
71
+ continue;
72
+ }
73
+ result.basicTypes.push(type);
56
74
  }
57
- return resolvedType;
75
+ return result;
58
76
  }
59
- function compareTypes(leftType, rightType, rightNode, context) {
60
- const allLeftTypes = tsutils.unionConstituents(leftType);
61
- const allRightTypes = tsutils.unionConstituents(rightType);
62
- if (allLeftTypes.some((t) => t.getStringIndexType() !== undefined ||
63
- (t.getNumberIndexType() !== undefined &&
64
- t.symbol?.name !== "Array"))) {
77
+ function report(properties, parentNode, context, services) {
78
+ const propertyNodes = properties.map((p) => p.valueDeclaration
79
+ ? services.tsNodeToESTreeNodeMap.get(p.valueDeclaration)
80
+ : null);
81
+ if (propertyNodes.every((p) => p && isChildNode(p, parentNode))) {
82
+ for (let i = 0; i < properties.length; i++) {
83
+ context.report({
84
+ data: { excessPropertyName: properties[i].name },
85
+ messageId: "noExcessProperty",
86
+ node: propertyNodes[i] ?? parentNode,
87
+ });
88
+ }
89
+ return;
90
+ }
91
+ if (properties.length > 1) {
92
+ context.report({
93
+ data: { excessPropertyNames: properties.map((p) => p.name).join(", ") },
94
+ messageId: "noExcessProperties",
95
+ node: parentNode,
96
+ });
65
97
  return;
66
98
  }
67
- for (const rightType of allRightTypes) {
68
- const rightResolvedType = resolveType(rightType);
69
- if (!isObjectLiteral(rightResolvedType)) {
99
+ context.report({
100
+ data: { excessPropertyName: properties[0].name },
101
+ messageId: "noExcessProperty",
102
+ node: parentNode,
103
+ });
104
+ }
105
+ function compareTypes(leftTypes, rightTypes, rightNode, context, services) {
106
+ const allLeftTypes = splitTypes(leftTypes.flatMap((t) => tsutils.unionConstituents(t)));
107
+ const allRightTypes = splitTypes(rightTypes.flatMap((t) => tsutils.unionConstituents(t)));
108
+ for (let i = 0; i < allLeftTypes.advancedTypes.length; i++) {
109
+ if (allLeftTypes.advancedTypes[i].length > 0 &&
110
+ allRightTypes.advancedTypes[i].length > 0) {
111
+ compareTypes(allLeftTypes.advancedTypes[i], allRightTypes.advancedTypes[i], rightNode, context, services);
112
+ }
113
+ }
114
+ for (const rightType of allRightTypes.basicTypes) {
115
+ if (!isObjectLiteral(rightType)) {
70
116
  continue;
71
117
  }
72
- const rightPropertyNames = rightResolvedType
73
- .getProperties()
74
- .map((p) => p.name);
75
- let bestMatchExcessPropertyNames = null;
76
- for (const leftType of allLeftTypes) {
77
- const leftResolvedType = resolveType(leftType);
78
- const leftPropertyNames = leftResolvedType
79
- .getProperties()
80
- .map((p) => p.name);
81
- const excessPropertyNames = rightPropertyNames.filter((n) => !leftPropertyNames.includes(n));
118
+ const rightProperties = rightType.getProperties();
119
+ let bestMatchExcessProperties = null;
120
+ for (const leftType of allLeftTypes.basicTypes) {
121
+ const leftPropertyNames = leftType.getProperties().map((p) => p.name);
122
+ if (leftType.getStringIndexType() !== undefined ||
123
+ leftType.getNumberIndexType() !== undefined) {
124
+ bestMatchExcessProperties = null;
125
+ break;
126
+ }
127
+ const excessProperties = rightProperties.filter((p) => !leftPropertyNames.includes(p.name));
82
128
  if (leftPropertyNames.length > 0 &&
83
- (bestMatchExcessPropertyNames === null ||
84
- excessPropertyNames.length < bestMatchExcessPropertyNames.length)) {
85
- bestMatchExcessPropertyNames = excessPropertyNames;
129
+ (bestMatchExcessProperties === null ||
130
+ excessProperties.length < bestMatchExcessProperties.length)) {
131
+ bestMatchExcessProperties = excessProperties;
86
132
  }
87
133
  }
88
- if (bestMatchExcessPropertyNames &&
89
- bestMatchExcessPropertyNames.length > 0) {
90
- context.report({
91
- data: {
92
- excessPropertyNames: bestMatchExcessPropertyNames.join(", "),
93
- },
94
- messageId: "noExcessProperties",
95
- node: rightNode,
96
- });
134
+ if (bestMatchExcessProperties && bestMatchExcessProperties.length > 0) {
135
+ report(bestMatchExcessProperties, rightNode, context, services);
97
136
  }
98
137
  }
99
138
  }
@@ -105,7 +144,7 @@ const noExcessProperties = createRule({
105
144
  AssignmentExpression(node) {
106
145
  const leftType = services.getTypeAtLocation(node.left);
107
146
  const rightType = services.getTypeAtLocation(node.right);
108
- compareTypes(leftType, rightType, node.right, context);
147
+ compareTypes([leftType], [rightType], node.right, context, services);
109
148
  },
110
149
  CallExpression(node) {
111
150
  if (node.arguments.length <= 0) {
@@ -129,7 +168,7 @@ const noExcessProperties = createRule({
129
168
  paramType = arrayType;
130
169
  }
131
170
  }
132
- compareTypes(paramType, argType, node.arguments[i], context);
171
+ compareTypes([paramType], [argType], node.arguments[i], context, services);
133
172
  }
134
173
  },
135
174
  Property(node) {
@@ -142,7 +181,7 @@ const noExcessProperties = createRule({
142
181
  if (!leftType) {
143
182
  return;
144
183
  }
145
- compareTypes(leftType, rightType, node, context);
184
+ compareTypes([leftType], [rightType], node, context, services);
146
185
  },
147
186
  ReturnStatement(node) {
148
187
  if (!node.argument) {
@@ -164,7 +203,7 @@ const noExcessProperties = createRule({
164
203
  }
165
204
  }
166
205
  const argType = services.getTypeAtLocation(node.argument);
167
- compareTypes(returnType, argType, node.argument, context);
206
+ compareTypes([returnType], [argType], node.argument, context, services);
168
207
  },
169
208
  VariableDeclarator(node) {
170
209
  if (!node.id.typeAnnotation || !node.init) {
@@ -172,7 +211,7 @@ const noExcessProperties = createRule({
172
211
  }
173
212
  const leftType = services.getTypeAtLocation(node.id.typeAnnotation.typeAnnotation);
174
213
  const rightType = services.getTypeAtLocation(node.init);
175
- compareTypes(leftType, rightType, node.init, context);
214
+ compareTypes([leftType], [rightType], node.init, context, services);
176
215
  },
177
216
  };
178
217
  },
@@ -184,6 +223,7 @@ const noExcessProperties = createRule({
184
223
  requiresTypeChecking: true,
185
224
  },
186
225
  messages: {
226
+ noExcessProperty: "Excess property '{{ excessPropertyName }}' found",
187
227
  noExcessProperties: "Excess properties '{{ excessPropertyNames }}' found",
188
228
  },
189
229
  schema: [],
@@ -1 +1 @@
1
- {"version":3,"file":"object-literal.js","sourceRoot":"","sources":["../src/object-literal.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"object-literal.js","sourceRoot":"","sources":["../src/object-literal.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "eslint-plugin-no-excess-properties",
3
3
  "description": "Excess properties are not allowed on object literals",
4
4
  "license": "MIT",
5
- "version": "0.0.7",
5
+ "version": "0.0.9",
6
6
  "homepage": "https://bitbucket.org/unimorphic/eslint-plugin-no-excess-properties",
7
7
  "keywords": [
8
8
  "eslint-plugin",
@@ -8,18 +8,19 @@ RuleTester.it = vitest.it;
8
8
  RuleTester.itOnly = vitest.it.only;
9
9
  RuleTester.describe = vitest.describe;
10
10
 
11
- function createError(props: {
12
- line: number;
13
- column: number;
14
- endColumn: number;
15
- endLine?: number;
16
- }): TestCaseError<"noExcessProperties"> {
11
+ function createError(
12
+ props: Partial<TestCaseError<"noExcessProperties" | "noExcessProperty">> & {
13
+ line: number;
14
+ column: number;
15
+ endColumn: number;
16
+ isMulti?: boolean;
17
+ },
18
+ ): TestCaseError<"noExcessProperties" | "noExcessProperty"> {
19
+ const { endLine, isMulti, ...otherProps } = props;
17
20
  return {
18
- column: props.column,
19
- endColumn: props.endColumn,
20
- endLine: props.endLine ?? props.line,
21
- line: props.line,
22
- messageId: "noExcessProperties",
21
+ ...otherProps,
22
+ endLine: endLine ?? props.line,
23
+ messageId: isMulti ? "noExcessProperties" : "noExcessProperty",
23
24
  };
24
25
  }
25
26
 
@@ -87,7 +88,7 @@ ruleTester.run("object-literal", objectLiteral, {
87
88
  code: `
88
89
  const test: () => { prop1: number; } = () => ({ prop1: 1, prop2: 2 })
89
90
  `,
90
- errors: [createError({ column: 48, endColumn: 78, line: 2 })],
91
+ errors: [createError({ column: 67, endColumn: 75, line: 2 })],
91
92
  },
92
93
  {
93
94
  code: `
@@ -127,21 +128,21 @@ ruleTester.run("object-literal", objectLiteral, {
127
128
  function test(param1: { prop1: number }) {}
128
129
  test({ prop1: 1, prop2: 2 });
129
130
  `,
130
- errors: [createError({ column: 14, endColumn: 36, line: 3 })],
131
+ errors: [createError({ column: 26, endColumn: 34, line: 3 })],
131
132
  },
132
133
  {
133
134
  code: `
134
135
  function test(param1: () => { prop1: number }) {}
135
136
  test(() => ({ prop1: 1, prop2: 2 }));
136
137
  `,
137
- errors: [createError({ column: 14, endColumn: 44, line: 3 })],
138
+ errors: [createError({ column: 33, endColumn: 41, line: 3 })],
138
139
  },
139
140
  {
140
141
  code: `
141
142
  function test(param1: number, param2: { prop1: number } | null) {}
142
143
  test(1, true ? { prop1: 1, prop2: 2 } : null);
143
144
  `,
144
- errors: [createError({ column: 17, endColumn: 53, line: 3 })],
145
+ errors: [createError({ column: 36, endColumn: 44, line: 3 })],
145
146
  },
146
147
  ],
147
148
  });
@@ -190,14 +191,14 @@ ruleTester.run("object-literal", objectLiteral, {
190
191
  code: `
191
192
  const test1: { prop1: 1 }[] = [{ prop1: 1 }].map(a => ({ ...a, prop2: 2 }))
192
193
  `,
193
- errors: [createError({ column: 39, endColumn: 84, line: 2 })],
194
+ errors: [createError({ column: 72, endColumn: 80, line: 2 })],
194
195
  },
195
196
  {
196
197
  code: `
197
198
  const test: { prop1: number; }[] = [];
198
199
  test.push({ prop1: 1, prop: 2 })
199
200
  `,
200
- errors: [createError({ column: 19, endColumn: 40, line: 3 })],
201
+ errors: [createError({ column: 31, endColumn: 38, line: 3 })],
201
202
  },
202
203
  ],
203
204
  });
@@ -227,28 +228,28 @@ ruleTester.run("object-literal", objectLiteral, {
227
228
  function test(param1: { prop1: number }[] | (() => void)) {}
228
229
  test([{ prop1: 1, prop2: 2 }]);
229
230
  `,
230
- errors: [createError({ column: 14, endColumn: 38, line: 3 })],
231
+ errors: [createError({ column: 27, endColumn: 35, line: 3 })],
231
232
  },
232
233
  {
233
234
  code: `
234
235
  function test(param1: { prop1: number }[] | (() => { prop1: number })) {}
235
236
  test(() => ({ prop1: 1, prop2: 2 }));
236
237
  `,
237
- errors: [createError({ column: 14, endColumn: 44, line: 3 })],
238
+ errors: [createError({ column: 33, endColumn: 41, line: 3 })],
238
239
  },
239
240
  {
240
241
  code: `
241
242
  function test(param1: { prop1: number } | { prop1: number, prop2: number }) {}
242
243
  test({ prop1: 1, prop2: 2, prop3: 3 });
243
244
  `,
244
- errors: [createError({ column: 14, endColumn: 46, line: 3 })],
245
+ errors: [createError({ column: 36, endColumn: 44, line: 3 })],
245
246
  },
246
247
  {
247
248
  code: `
248
249
  function test(param1: { prop1: number } | { prop2: number }) {}
249
250
  test({ prop1: 1, prop2: 2 });
250
251
  `,
251
- errors: [createError({ column: 14, endColumn: 36, line: 3 })],
252
+ errors: [createError({ column: 26, endColumn: 34, line: 3 })],
252
253
  },
253
254
  ],
254
255
  });
@@ -266,7 +267,78 @@ ruleTester.run("object-literal", objectLiteral, {
266
267
  function test(param1: { prop1: number } & { prop2: number }) {}
267
268
  test({ prop1: 1, prop2: 2, prop3: 3 });
268
269
  `,
269
- errors: [createError({ column: 14, endColumn: 46, line: 3 })],
270
+ errors: [createError({ column: 36, endColumn: 44, line: 3 })],
271
+ },
272
+ ],
273
+ });
274
+
275
+ ruleTester.run("object-literal", objectLiteral, {
276
+ valid: [
277
+ `
278
+ function test(param1: ({ prop1: number } | { prop2: number })[]) {}
279
+ test([{ prop2: 2 }]);
280
+ `,
281
+ `
282
+ function test(param1: () => ({ prop1: number } | { prop2: number } | null)[]) {}
283
+ test(() => [true ? { prop2: 2 } : null]);
284
+ `,
285
+ ],
286
+ invalid: [
287
+ {
288
+ code: `
289
+ function test(param1: ({ prop1: number } | { prop2: number })[]) {}
290
+ test([{ prop1: 1, prop2: 2 }]);
291
+ `,
292
+ errors: [createError({ column: 27, endColumn: 35, line: 3 })],
293
+ },
294
+ {
295
+ code: `
296
+ function test(param1: () => ({ prop1: number } | { prop2: number } | null)[]) {}
297
+ test(() => [true ? { prop1: 1, prop2: 2 } : null]);
298
+ `,
299
+ errors: [createError({ column: 40, endColumn: 48, line: 3 })],
300
+ },
301
+ ],
302
+ });
303
+
304
+ ruleTester.run("object-literal", objectLiteral, {
305
+ valid: [],
306
+ invalid: [
307
+ {
308
+ code: `
309
+ function test(param1: { prop1: number }) {}
310
+ test({ prop1: 1, prop2: 2, prop3: 3 });
311
+ `,
312
+ errors: [
313
+ createError({
314
+ column: 26,
315
+ data: { excessPropertyName: "prop2" },
316
+ endColumn: 34,
317
+ line: 3,
318
+ }),
319
+ createError({
320
+ column: 36,
321
+ data: { excessPropertyName: "prop3" },
322
+ endColumn: 44,
323
+ line: 3,
324
+ }),
325
+ ],
326
+ },
327
+ {
328
+ code: `
329
+ let test1: { prop1: number; } = { prop1: 1 };
330
+ const test2 = { prop1: 2, prop2: 3, prop3: 4 };
331
+ test1 = test2;
332
+ `,
333
+ errors: [
334
+ createError({
335
+ column: 17,
336
+ data: { excessPropertyNames: "prop2, prop3" },
337
+ endColumn: 22,
338
+ line: 4,
339
+ isMulti: true,
340
+ }),
341
+ ],
270
342
  },
271
343
  ],
272
344
  });
@@ -291,6 +363,9 @@ ruleTester.run("object-literal", objectLiteral, {
291
363
  `
292
364
  Object.keys({ prop1: 1 })
293
365
  `,
366
+ `
367
+ const test: { prop1: number, [property: string]: number; }[] = [{ prop2: 1 }];
368
+ `,
294
369
  ],
295
370
  invalid: [],
296
371
  });
@@ -1,4 +1,9 @@
1
- import { ASTUtils, ESLintUtils, TSESTree } from "@typescript-eslint/utils";
1
+ import {
2
+ ASTUtils,
3
+ ESLintUtils,
4
+ ParserServicesWithTypeInformation,
5
+ TSESTree,
6
+ } from "@typescript-eslint/utils";
2
7
  import { RuleContext } from "@typescript-eslint/utils/ts-eslint";
3
8
  import ts from "typescript";
4
9
  import * as tsutils from "ts-api-utils";
@@ -24,84 +29,144 @@ function isObjectLiteral(type: ts.Type): boolean {
24
29
  );
25
30
  }
26
31
 
27
- function resolveType(type: ts.Type) {
28
- let resolvedType = type;
32
+ function isChildNode(node: TSESTree.Node, parentNode: TSESTree.Node) {
33
+ let parent: TSESTree.Node | undefined = node;
34
+ do {
35
+ parent = parent.parent;
36
+ if (parent === parentNode) {
37
+ return true;
38
+ }
39
+ } while (parent);
40
+ return false;
41
+ }
42
+
43
+ function splitTypes(types: ts.Type[]) {
44
+ const result = {
45
+ advancedTypes: [[], []] as ts.Type[][],
46
+ basicTypes: [] as ts.Type[],
47
+ };
48
+
49
+ for (const type of types) {
50
+ const callSignatures = type.getCallSignatures();
51
+ if (callSignatures.length === 1) {
52
+ result.advancedTypes[0].push(callSignatures[0].getReturnType());
53
+ continue;
54
+ }
29
55
 
30
- const callSignatures = resolvedType.getCallSignatures();
31
- if (callSignatures.length === 1) {
32
- resolvedType = callSignatures[0].getReturnType();
56
+ const arrayType = type.getNumberIndexType();
57
+ if (arrayType && (type as TypeOptionalSymbol).symbol?.name === "Array") {
58
+ result.advancedTypes[1].push(arrayType);
59
+ continue;
60
+ }
61
+
62
+ result.basicTypes.push(type);
63
+ }
64
+
65
+ return result;
66
+ }
67
+
68
+ function report(
69
+ properties: ts.Symbol[],
70
+ parentNode: TSESTree.Node,
71
+ context: Readonly<RuleContext<"noExcessProperties" | "noExcessProperty", []>>,
72
+ services: ParserServicesWithTypeInformation,
73
+ ) {
74
+ const propertyNodes = properties.map((p) =>
75
+ p.valueDeclaration
76
+ ? services.tsNodeToESTreeNodeMap.get(p.valueDeclaration)
77
+ : null,
78
+ );
79
+
80
+ if (propertyNodes.every((p) => p && isChildNode(p, parentNode))) {
81
+ for (let i = 0; i < properties.length; i++) {
82
+ context.report({
83
+ data: { excessPropertyName: properties[i].name },
84
+ messageId: "noExcessProperty",
85
+ node: propertyNodes[i] ?? parentNode,
86
+ });
87
+ }
88
+ return;
33
89
  }
34
90
 
35
- const arrayType = resolvedType.getNumberIndexType();
36
- if (arrayType) {
37
- resolvedType = arrayType;
91
+ if (properties.length > 1) {
92
+ context.report({
93
+ data: { excessPropertyNames: properties.map((p) => p.name).join(", ") },
94
+ messageId: "noExcessProperties",
95
+ node: parentNode,
96
+ });
97
+ return;
38
98
  }
39
99
 
40
- return resolvedType;
100
+ context.report({
101
+ data: { excessPropertyName: properties[0].name },
102
+ messageId: "noExcessProperty",
103
+ node: parentNode,
104
+ });
41
105
  }
42
106
 
43
107
  function compareTypes(
44
- leftType: ts.Type,
45
- rightType: ts.Type,
108
+ leftTypes: ts.Type[],
109
+ rightTypes: ts.Type[],
46
110
  rightNode: TSESTree.Node,
47
- context: Readonly<RuleContext<"noExcessProperties", []>>,
111
+ context: Readonly<RuleContext<"noExcessProperties" | "noExcessProperty", []>>,
112
+ services: ParserServicesWithTypeInformation,
48
113
  ): void {
49
- const allLeftTypes = tsutils.unionConstituents(leftType);
50
- const allRightTypes = tsutils.unionConstituents(rightType);
51
-
52
- if (
53
- allLeftTypes.some(
54
- (t) =>
55
- t.getStringIndexType() !== undefined ||
56
- (t.getNumberIndexType() !== undefined &&
57
- (t as TypeOptionalSymbol).symbol?.name !== "Array"),
58
- )
59
- ) {
60
- return;
61
- }
114
+ const allLeftTypes = splitTypes(
115
+ leftTypes.flatMap((t) => tsutils.unionConstituents(t)),
116
+ );
117
+ const allRightTypes = splitTypes(
118
+ rightTypes.flatMap((t) => tsutils.unionConstituents(t)),
119
+ );
62
120
 
63
- for (const rightType of allRightTypes) {
64
- const rightResolvedType = resolveType(rightType);
121
+ for (let i = 0; i < allLeftTypes.advancedTypes.length; i++) {
122
+ if (
123
+ allLeftTypes.advancedTypes[i].length > 0 &&
124
+ allRightTypes.advancedTypes[i].length > 0
125
+ ) {
126
+ compareTypes(
127
+ allLeftTypes.advancedTypes[i],
128
+ allRightTypes.advancedTypes[i],
129
+ rightNode,
130
+ context,
131
+ services,
132
+ );
133
+ }
134
+ }
65
135
 
66
- if (!isObjectLiteral(rightResolvedType)) {
136
+ for (const rightType of allRightTypes.basicTypes) {
137
+ if (!isObjectLiteral(rightType)) {
67
138
  continue;
68
139
  }
69
140
 
70
- const rightPropertyNames = rightResolvedType
71
- .getProperties()
72
- .map((p) => p.name);
141
+ const rightProperties = rightType.getProperties();
73
142
 
74
- let bestMatchExcessPropertyNames: string[] | null = null;
75
- for (const leftType of allLeftTypes) {
76
- const leftResolvedType = resolveType(leftType);
77
- const leftPropertyNames = leftResolvedType
78
- .getProperties()
79
- .map((p) => p.name);
143
+ let bestMatchExcessProperties: ts.Symbol[] | null = null;
144
+ for (const leftType of allLeftTypes.basicTypes) {
145
+ const leftPropertyNames = leftType.getProperties().map((p) => p.name);
80
146
 
81
- const excessPropertyNames = rightPropertyNames.filter(
82
- (n) => !leftPropertyNames.includes(n),
147
+ if (
148
+ leftType.getStringIndexType() !== undefined ||
149
+ leftType.getNumberIndexType() !== undefined
150
+ ) {
151
+ bestMatchExcessProperties = null;
152
+ break;
153
+ }
154
+
155
+ const excessProperties = rightProperties.filter(
156
+ (p) => !leftPropertyNames.includes(p.name),
83
157
  );
84
158
 
85
159
  if (
86
160
  leftPropertyNames.length > 0 &&
87
- (bestMatchExcessPropertyNames === null ||
88
- excessPropertyNames.length < bestMatchExcessPropertyNames.length)
161
+ (bestMatchExcessProperties === null ||
162
+ excessProperties.length < bestMatchExcessProperties.length)
89
163
  ) {
90
- bestMatchExcessPropertyNames = excessPropertyNames;
164
+ bestMatchExcessProperties = excessProperties;
91
165
  }
92
166
  }
93
167
 
94
- if (
95
- bestMatchExcessPropertyNames &&
96
- bestMatchExcessPropertyNames.length > 0
97
- ) {
98
- context.report({
99
- data: {
100
- excessPropertyNames: bestMatchExcessPropertyNames.join(", "),
101
- },
102
- messageId: "noExcessProperties",
103
- node: rightNode,
104
- });
168
+ if (bestMatchExcessProperties && bestMatchExcessProperties.length > 0) {
169
+ report(bestMatchExcessProperties, rightNode, context, services);
105
170
  }
106
171
  }
107
172
  }
@@ -116,7 +181,7 @@ const noExcessProperties = createRule({
116
181
  const leftType = services.getTypeAtLocation(node.left);
117
182
  const rightType = services.getTypeAtLocation(node.right);
118
183
 
119
- compareTypes(leftType, rightType, node.right, context);
184
+ compareTypes([leftType], [rightType], node.right, context, services);
120
185
  },
121
186
  CallExpression(node) {
122
187
  if (node.arguments.length <= 0) {
@@ -152,7 +217,13 @@ const noExcessProperties = createRule({
152
217
  }
153
218
  }
154
219
 
155
- compareTypes(paramType, argType, node.arguments[i], context);
220
+ compareTypes(
221
+ [paramType],
222
+ [argType],
223
+ node.arguments[i],
224
+ context,
225
+ services,
226
+ );
156
227
  }
157
228
  },
158
229
  Property(node) {
@@ -169,7 +240,7 @@ const noExcessProperties = createRule({
169
240
  return;
170
241
  }
171
242
 
172
- compareTypes(leftType, rightType, node, context);
243
+ compareTypes([leftType], [rightType], node, context, services);
173
244
  },
174
245
  ReturnStatement(node) {
175
246
  if (!node.argument) {
@@ -200,7 +271,7 @@ const noExcessProperties = createRule({
200
271
 
201
272
  const argType = services.getTypeAtLocation(node.argument);
202
273
 
203
- compareTypes(returnType, argType, node.argument, context);
274
+ compareTypes([returnType], [argType], node.argument, context, services);
204
275
  },
205
276
  VariableDeclarator(node) {
206
277
  if (!node.id.typeAnnotation || !node.init) {
@@ -212,7 +283,7 @@ const noExcessProperties = createRule({
212
283
  );
213
284
  const rightType = services.getTypeAtLocation(node.init);
214
285
 
215
- compareTypes(leftType, rightType, node.init, context);
286
+ compareTypes([leftType], [rightType], node.init, context, services);
216
287
  },
217
288
  };
218
289
  },
@@ -224,6 +295,7 @@ const noExcessProperties = createRule({
224
295
  requiresTypeChecking: true,
225
296
  },
226
297
  messages: {
298
+ noExcessProperty: "Excess property '{{ excessPropertyName }}' found",
227
299
  noExcessProperties: "Excess properties '{{ excessPropertyNames }}' found",
228
300
  },
229
301
  schema: [],