Skip to content

Commit 10f8a4d

Browse files
authored
fix(eslint-plugin): [no-unnecessary-type-parameters] descend into all parts of mapped types in no-unnecessary-type-parameters (#9530)
1 parent 8ceca98 commit 10f8a4d

File tree

2 files changed

+24
-1
lines changed

2 files changed

+24
-1
lines changed

packages/eslint-plugin/src/rules/no-unnecessary-type-parameters.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,10 +271,16 @@ function collectTypeParameterUsageCounts(
271271
// `isTypeReference` to avoid descending into all the properties of a
272272
// generic interface/class, e.g. `Map<K, V>`.
273273
else if (tsutils.isObjectType(type)) {
274-
visitSymbolsListOnce(type.getProperties(), false);
274+
const properties = type.getProperties();
275+
visitSymbolsListOnce(properties, false);
275276

276277
if (isMappedType(type)) {
277278
visitType(type.typeParameter, false);
279+
if (properties.length === 0) {
280+
// TS treats mapped types like `{[k in "a"]: T}` like `{a: T}`.
281+
// They have properties, so we need to avoid double-counting.
282+
visitType(type.templateType, false);
283+
}
278284
}
279285

280286
for (const typeArgument of type.aliasTypeArguments ?? []) {
@@ -372,6 +378,8 @@ function collectTypeParameterUsageCounts(
372378

373379
interface MappedType extends ts.ObjectType {
374380
typeParameter?: ts.Type;
381+
constraintType?: ts.Type;
382+
templateType?: ts.Type;
375383
}
376384

377385
function isMappedType(type: ts.Type): type is MappedType {

packages/eslint-plugin/tests/rules/no-unnecessary-type-parameters.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,12 @@ ruleTester.run('no-unnecessary-type-parameters', rule, {
342342
(token): token is Exclude<TSESTree.Token, Conditions & ExtractedToken> =>
343343
tokenType in conditions;
344344
`,
345+
`
346+
declare function mapObj<K extends string, V>(
347+
obj: { [key in K]?: V },
348+
fn: (key: K, val: V) => number,
349+
): number[];
350+
`,
345351
],
346352

347353
invalid: [
@@ -608,5 +614,14 @@ ruleTester.run('no-unnecessary-type-parameters', rule, {
608614
code: 'type Fn = <T extends string>() => `a${T}b`;',
609615
errors: [{ messageId: 'sole', data: { name: 'T' } }],
610616
},
617+
{
618+
code: `
619+
declare function mapObj<K extends string, V>(
620+
obj: { [key in K]?: V },
621+
fn: (key: K) => number,
622+
): number[];
623+
`,
624+
errors: [{ messageId: 'sole', data: { name: 'V' } }],
625+
},
611626
],
612627
});

0 commit comments

Comments
 (0)