diff --git a/packages/eslint-plugin/tests/rules/no-unused-vars.test.ts b/packages/eslint-plugin/tests/rules/no-unused-vars.test.ts
index 323d85b94a6..5165ecda28b 100644
--- a/packages/eslint-plugin/tests/rules/no-unused-vars.test.ts
+++ b/packages/eslint-plugin/tests/rules/no-unused-vars.test.ts
@@ -767,6 +767,14 @@ export abstract class Foo {
protected abstract readonly type: FooType;
}
`,
+ // https://github.com/typescript-eslint/typescript-eslint/issues/2449
+ `
+export type F = (...a: A) => unknown;
+ `,
+ `
+import { Foo } from './bar';
+export type F = (...a: Foo) => unknown;
+ `,
],
invalid: [
diff --git a/packages/scope-manager/src/referencer/TypeVisitor.ts b/packages/scope-manager/src/referencer/TypeVisitor.ts
index 49f0ac12743..a43d0948659 100644
--- a/packages/scope-manager/src/referencer/TypeVisitor.ts
+++ b/packages/scope-manager/src/referencer/TypeVisitor.ts
@@ -33,6 +33,7 @@ class TypeVisitor extends Visitor {
this.visit(node.typeParameters);
for (const param of node.params) {
+ let didVisitAnnotation = false;
this.visitPattern(param, (pattern, info) => {
// a parameter name creates a value type variable which can be referenced later via typeof arg
this.#referencer
@@ -41,8 +42,17 @@ class TypeVisitor extends Visitor {
pattern,
new ParameterDefinition(pattern, node, info.rest),
);
- this.visit(pattern.typeAnnotation);
+
+ if (pattern.typeAnnotation) {
+ this.visit(pattern.typeAnnotation);
+ didVisitAnnotation = true;
+ }
});
+
+ // there are a few special cases where the type annotation is owned by the parameter, not the pattern
+ if (!didVisitAnnotation && 'typeAnnotation' in param) {
+ this.visit(param.typeAnnotation);
+ }
}
this.visit(node.returnType);
diff --git a/packages/scope-manager/tests/fixtures/type-declaration/function/params/array-pattern.ts b/packages/scope-manager/tests/fixtures/type-declaration/function/params/array-pattern.ts
new file mode 100644
index 00000000000..27da21ea7ad
--- /dev/null
+++ b/packages/scope-manager/tests/fixtures/type-declaration/function/params/array-pattern.ts
@@ -0,0 +1 @@
+type Fn = ([a]: A) => unknown;
diff --git a/packages/scope-manager/tests/fixtures/type-declaration/function/params/array-pattern.ts.shot b/packages/scope-manager/tests/fixtures/type-declaration/function/params/array-pattern.ts.shot
new file mode 100644
index 00000000000..409f49b66b9
--- /dev/null
+++ b/packages/scope-manager/tests/fixtures/type-declaration/function/params/array-pattern.ts.shot
@@ -0,0 +1,96 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`type-declaration function params array-pattern 1`] = `
+ScopeManager {
+ variables: Array [
+ Variable$1 {
+ defs: Array [
+ TypeDefinition$1 {
+ name: Identifier<"Fn">,
+ node: TSTypeAliasDeclaration$1,
+ },
+ ],
+ name: "Fn",
+ references: Array [],
+ isValueVariable: false,
+ isTypeVariable: true,
+ },
+ Variable$2 {
+ defs: Array [
+ TypeDefinition$2 {
+ name: Identifier<"A">,
+ node: TSTypeParameter$2,
+ },
+ ],
+ name: "A",
+ references: Array [
+ Reference$1 {
+ identifier: Identifier<"A">,
+ isRead: true,
+ isTypeReference: true,
+ isValueReference: false,
+ isWrite: false,
+ resolved: Variable$2,
+ },
+ ],
+ isValueVariable: false,
+ isTypeVariable: true,
+ },
+ Variable$3 {
+ defs: Array [
+ ParameterDefinition$3 {
+ name: Identifier<"a">,
+ node: TSFunctionType$3,
+ },
+ ],
+ name: "a",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ ],
+ scopes: Array [
+ GlobalScope$1 {
+ block: Program$4,
+ isStrict: false,
+ references: Array [],
+ set: Map {
+ "Fn" => Variable$1,
+ },
+ type: "global",
+ upper: null,
+ variables: Array [
+ Variable$1,
+ ],
+ },
+ TypeScope$2 {
+ block: TSTypeAliasDeclaration$1,
+ isStrict: true,
+ references: Array [],
+ set: Map {
+ "A" => Variable$2,
+ },
+ type: "type",
+ upper: GlobalScope$1,
+ variables: Array [
+ Variable$2,
+ ],
+ },
+ FunctionTypeScope$3 {
+ block: TSFunctionType$3,
+ isStrict: true,
+ references: Array [
+ Reference$1,
+ ],
+ set: Map {
+ "a" => Variable$3,
+ },
+ type: "functionType",
+ upper: TypeScope$2,
+ variables: Array [
+ Variable$3,
+ ],
+ },
+ ],
+}
+`;
diff --git a/packages/scope-manager/tests/fixtures/type-declaration/function/params/object-pattern.ts b/packages/scope-manager/tests/fixtures/type-declaration/function/params/object-pattern.ts
new file mode 100644
index 00000000000..f262383a8d8
--- /dev/null
+++ b/packages/scope-manager/tests/fixtures/type-declaration/function/params/object-pattern.ts
@@ -0,0 +1 @@
+type Fn = ({ a }: A) => unknown;
diff --git a/packages/scope-manager/tests/fixtures/type-declaration/function/params/object-pattern.ts.shot b/packages/scope-manager/tests/fixtures/type-declaration/function/params/object-pattern.ts.shot
new file mode 100644
index 00000000000..44d53bab98e
--- /dev/null
+++ b/packages/scope-manager/tests/fixtures/type-declaration/function/params/object-pattern.ts.shot
@@ -0,0 +1,96 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`type-declaration function params object-pattern 1`] = `
+ScopeManager {
+ variables: Array [
+ Variable$1 {
+ defs: Array [
+ TypeDefinition$1 {
+ name: Identifier<"Fn">,
+ node: TSTypeAliasDeclaration$1,
+ },
+ ],
+ name: "Fn",
+ references: Array [],
+ isValueVariable: false,
+ isTypeVariable: true,
+ },
+ Variable$2 {
+ defs: Array [
+ TypeDefinition$2 {
+ name: Identifier<"A">,
+ node: TSTypeParameter$2,
+ },
+ ],
+ name: "A",
+ references: Array [
+ Reference$1 {
+ identifier: Identifier<"A">,
+ isRead: true,
+ isTypeReference: true,
+ isValueReference: false,
+ isWrite: false,
+ resolved: Variable$2,
+ },
+ ],
+ isValueVariable: false,
+ isTypeVariable: true,
+ },
+ Variable$3 {
+ defs: Array [
+ ParameterDefinition$3 {
+ name: Identifier<"a">,
+ node: TSFunctionType$3,
+ },
+ ],
+ name: "a",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ ],
+ scopes: Array [
+ GlobalScope$1 {
+ block: Program$4,
+ isStrict: false,
+ references: Array [],
+ set: Map {
+ "Fn" => Variable$1,
+ },
+ type: "global",
+ upper: null,
+ variables: Array [
+ Variable$1,
+ ],
+ },
+ TypeScope$2 {
+ block: TSTypeAliasDeclaration$1,
+ isStrict: true,
+ references: Array [],
+ set: Map {
+ "A" => Variable$2,
+ },
+ type: "type",
+ upper: GlobalScope$1,
+ variables: Array [
+ Variable$2,
+ ],
+ },
+ FunctionTypeScope$3 {
+ block: TSFunctionType$3,
+ isStrict: true,
+ references: Array [
+ Reference$1,
+ ],
+ set: Map {
+ "a" => Variable$3,
+ },
+ type: "functionType",
+ upper: TypeScope$2,
+ variables: Array [
+ Variable$3,
+ ],
+ },
+ ],
+}
+`;
diff --git a/packages/scope-manager/tests/fixtures/type-declaration/function/params/rest-element.ts b/packages/scope-manager/tests/fixtures/type-declaration/function/params/rest-element.ts
new file mode 100644
index 00000000000..da11f73c40c
--- /dev/null
+++ b/packages/scope-manager/tests/fixtures/type-declaration/function/params/rest-element.ts
@@ -0,0 +1 @@
+type Fn = (...a: A) => unknown;
diff --git a/packages/scope-manager/tests/fixtures/type-declaration/function/params/rest-element.ts.shot b/packages/scope-manager/tests/fixtures/type-declaration/function/params/rest-element.ts.shot
new file mode 100644
index 00000000000..6b69e7667d7
--- /dev/null
+++ b/packages/scope-manager/tests/fixtures/type-declaration/function/params/rest-element.ts.shot
@@ -0,0 +1,96 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`type-declaration function params rest-element 1`] = `
+ScopeManager {
+ variables: Array [
+ Variable$1 {
+ defs: Array [
+ TypeDefinition$1 {
+ name: Identifier<"Fn">,
+ node: TSTypeAliasDeclaration$1,
+ },
+ ],
+ name: "Fn",
+ references: Array [],
+ isValueVariable: false,
+ isTypeVariable: true,
+ },
+ Variable$2 {
+ defs: Array [
+ TypeDefinition$2 {
+ name: Identifier<"A">,
+ node: TSTypeParameter$2,
+ },
+ ],
+ name: "A",
+ references: Array [
+ Reference$1 {
+ identifier: Identifier<"A">,
+ isRead: true,
+ isTypeReference: true,
+ isValueReference: false,
+ isWrite: false,
+ resolved: Variable$2,
+ },
+ ],
+ isValueVariable: false,
+ isTypeVariable: true,
+ },
+ Variable$3 {
+ defs: Array [
+ ParameterDefinition$3 {
+ name: Identifier<"a">,
+ node: TSFunctionType$3,
+ },
+ ],
+ name: "a",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ ],
+ scopes: Array [
+ GlobalScope$1 {
+ block: Program$4,
+ isStrict: false,
+ references: Array [],
+ set: Map {
+ "Fn" => Variable$1,
+ },
+ type: "global",
+ upper: null,
+ variables: Array [
+ Variable$1,
+ ],
+ },
+ TypeScope$2 {
+ block: TSTypeAliasDeclaration$1,
+ isStrict: true,
+ references: Array [],
+ set: Map {
+ "A" => Variable$2,
+ },
+ type: "type",
+ upper: GlobalScope$1,
+ variables: Array [
+ Variable$2,
+ ],
+ },
+ FunctionTypeScope$3 {
+ block: TSFunctionType$3,
+ isStrict: true,
+ references: Array [
+ Reference$1,
+ ],
+ set: Map {
+ "a" => Variable$3,
+ },
+ type: "functionType",
+ upper: TypeScope$2,
+ variables: Array [
+ Variable$3,
+ ],
+ },
+ ],
+}
+`;