diff --git a/.README/rules/check-template-names.md b/.README/rules/check-template-names.md index f6a8832d..51e0f377 100644 --- a/.README/rules/check-template-names.md +++ b/.README/rules/check-template-names.md @@ -3,7 +3,7 @@ Checks that any `@template` names are actually used in the connected `@typedef` or type alias. -Currently checks `TSTypeAliasDeclaration` such as: +Currently checks `TSInterfaceDeclaration` or `TSTypeAliasDeclaration` such as: ```ts /** diff --git a/.README/rules/require-template.md b/.README/rules/require-template.md index 73bff6b8..2544f1d9 100644 --- a/.README/rules/require-template.md +++ b/.README/rules/require-template.md @@ -3,7 +3,7 @@ Checks to see that `@template` tags are present for any detected type parameters. -Currently checks `TSTypeAliasDeclaration` such as: +Currently checks `TSInterfaceDeclaration` or `TSTypeAliasDeclaration` such as: ```ts export type Pairs = [D, V | undefined]; diff --git a/docs/rules/check-template-names.md b/docs/rules/check-template-names.md index eecdc360..7249f041 100644 --- a/docs/rules/check-template-names.md +++ b/docs/rules/check-template-names.md @@ -5,7 +5,7 @@ Checks that any `@template` names are actually used in the connected `@typedef` or type alias. -Currently checks `TSTypeAliasDeclaration` such as: +Currently checks `TSInterfaceDeclaration` or `TSTypeAliasDeclaration` such as: ```ts /** @@ -95,6 +95,33 @@ export type Extras = [D, U | undefined]; * @property {V} foo */ // Message: @template D not in use + +/** + * @template D + * @template V + */ +interface GenericIdentityFn { + (arg: Type): Type; +} +// Message: @template D not in use + +/** + * @template D + * @template V + */ +export interface GenericIdentityFn { + (arg: Type): Type; +} +// Message: @template D not in use + +/** + * @template D + * @template V + */ +export default interface GenericIdentityFn { + (arg: Type): Type; +} +// Message: @template D not in use ```` @@ -146,5 +173,26 @@ export type Extras = [D, U, V | undefined]; * @property {D} foo * @property {V} bar */ + +/** + * @template Type + */ +interface GenericIdentityFn { + (arg: Type): Type; +} + +/** + * @template Type + */ +export interface GenericIdentityFn { + (arg: Type): Type; +} + +/** + * @template Type + */ +export default interface GenericIdentityFn { + (arg: Type): Type; +} ```` diff --git a/docs/rules/require-template.md b/docs/rules/require-template.md index a701606e..16d8e3f0 100644 --- a/docs/rules/require-template.md +++ b/docs/rules/require-template.md @@ -5,7 +5,7 @@ Checks to see that `@template` tags are present for any detected type parameters. -Currently checks `TSTypeAliasDeclaration` such as: +Currently checks `TSInterfaceDeclaration` or `TSTypeAliasDeclaration` such as: ```ts export type Pairs = [D, V | undefined]; @@ -114,6 +114,30 @@ export type Pairs = [D, V | undefined]; * @property {X} bar */ // Message: Missing @template D + +/** + * + */ +interface GenericIdentityFn { + (arg: Type): Type; +} +// Message: Missing @template Type + +/** + * + */ +export interface GenericIdentityFn { + (arg: Type): Type; +} +// Message: Missing @template Type + +/** + * + */ +export default interface GenericIdentityFn { + (arg: Type): Type; +} +// Message: Missing @template Type ```` @@ -164,5 +188,26 @@ export type Extras = [D, U, V | undefined]; * @property {D} foo * @property {V} bar */ + +/** + * @template Type + */ +interface GenericIdentityFn { + (arg: Type): Type; +} + +/** + * @template Type + */ +export interface GenericIdentityFn { + (arg: Type): Type; +} + +/** + * @template Type + */ +export default interface GenericIdentityFn { + (arg: Type): Type; +} ```` diff --git a/src/rules/checkTemplateNames.js b/src/rules/checkTemplateNames.js index a7480d0a..9a2914eb 100644 --- a/src/rules/checkTemplateNames.js +++ b/src/rules/checkTemplateNames.js @@ -20,7 +20,8 @@ export default iterateJsdoc(({ const usedNames = new Set(); /** - * @param {import('@typescript-eslint/types').TSESTree.TSTypeAliasDeclaration} aliasDeclaration + * @param {import('@typescript-eslint/types').TSESTree.TSInterfaceDeclaration| + * import('@typescript-eslint/types').TSESTree.TSTypeAliasDeclaration} aliasDeclaration */ const checkParameters = (aliasDeclaration) => { /* c8 ignore next -- Guard */ @@ -47,12 +48,17 @@ export default iterateJsdoc(({ return; } switch (nde.type) { + case 'ExportDefaultDeclaration': case 'ExportNamedDeclaration': - if (nde.declaration?.type === 'TSTypeAliasDeclaration') { - checkParameters(nde.declaration); + switch (nde.declaration?.type) { + case 'TSTypeAliasDeclaration': + case 'TSInterfaceDeclaration': + checkParameters(nde.declaration); + break; } break; case 'TSTypeAliasDeclaration': + case 'TSInterfaceDeclaration': checkParameters(nde); break; } diff --git a/src/rules/requireTemplate.js b/src/rules/requireTemplate.js index 6d4bf3e8..ebee54a0 100644 --- a/src/rules/requireTemplate.js +++ b/src/rules/requireTemplate.js @@ -35,9 +35,10 @@ export default iterateJsdoc(({ } /** - * @param {import('@typescript-eslint/types').TSESTree.TSTypeAliasDeclaration} aliasDeclaration + * @param {import('@typescript-eslint/types').TSESTree.TSInterfaceDeclaration| + * import('@typescript-eslint/types').TSESTree.TSTypeAliasDeclaration} aliasDeclaration */ - const checkParameters = (aliasDeclaration) => { + const checkTypeParams = (aliasDeclaration) => { /* c8 ignore next -- Guard */ const {params} = aliasDeclaration.typeParameters ?? {params: []}; for (const {name: {name}} of params) { @@ -50,7 +51,7 @@ export default iterateJsdoc(({ } }; - const handleTypeAliases = () => { + const handleTypes = () => { const nde = /** @type {import('@typescript-eslint/types').TSESTree.Node} */ ( node ); @@ -58,20 +59,31 @@ export default iterateJsdoc(({ return; } switch (nde.type) { + case 'ExportDefaultDeclaration': + switch (nde.declaration?.type) { + case 'TSInterfaceDeclaration': + checkTypeParams(nde.declaration); + break; + } + break; case 'ExportNamedDeclaration': - if (nde.declaration?.type === 'TSTypeAliasDeclaration') { - checkParameters(nde.declaration); + switch (nde.declaration?.type) { + case 'TSTypeAliasDeclaration': + case 'TSInterfaceDeclaration': + checkTypeParams(nde.declaration); + break; } break; case 'TSTypeAliasDeclaration': - checkParameters(nde); + case 'TSInterfaceDeclaration': + checkTypeParams(nde); break; } }; const typedefTags = utils.getTags('typedef'); if (!typedefTags.length || typedefTags.length >= 2) { - handleTypeAliases(); + handleTypes(); return; } diff --git a/test/rules/assertions/checkTemplateNames.js b/test/rules/assertions/checkTemplateNames.js index 4d8167a6..254a34f0 100644 --- a/test/rules/assertions/checkTemplateNames.js +++ b/test/rules/assertions/checkTemplateNames.js @@ -158,6 +158,78 @@ export default { }, ], }, + { + code: ` + /** + * @template D + * @template V + */ + interface GenericIdentityFn { + (arg: Type): Type; + } + `, + errors: [ + { + line: 3, + message: '@template D not in use', + }, + { + line: 4, + message: '@template V not in use', + }, + ], + languageOptions: { + parser: typescriptEslintParser + }, + }, + { + code: ` + /** + * @template D + * @template V + */ + export interface GenericIdentityFn { + (arg: Type): Type; + } + `, + errors: [ + { + line: 3, + message: '@template D not in use', + }, + { + line: 4, + message: '@template V not in use', + }, + ], + languageOptions: { + parser: typescriptEslintParser + }, + }, + { + code: ` + /** + * @template D + * @template V + */ + export default interface GenericIdentityFn { + (arg: Type): Type; + } + `, + errors: [ + { + line: 3, + message: '@template D not in use', + }, + { + line: 4, + message: '@template V not in use', + }, + ], + languageOptions: { + parser: typescriptEslintParser + }, + }, ], valid: [ { @@ -228,5 +300,44 @@ export default { */ `, }, + { + code: ` + /** + * @template Type + */ + interface GenericIdentityFn { + (arg: Type): Type; + } + `, + languageOptions: { + parser: typescriptEslintParser + }, + }, + { + code: ` + /** + * @template Type + */ + export interface GenericIdentityFn { + (arg: Type): Type; + } + `, + languageOptions: { + parser: typescriptEslintParser + }, + }, + { + code: ` + /** + * @template Type + */ + export default interface GenericIdentityFn { + (arg: Type): Type; + } + `, + languageOptions: { + parser: typescriptEslintParser + }, + }, ], }; diff --git a/test/rules/assertions/requireTemplate.js b/test/rules/assertions/requireTemplate.js index c4cc57c9..75677199 100644 --- a/test/rules/assertions/requireTemplate.js +++ b/test/rules/assertions/requireTemplate.js @@ -171,6 +171,63 @@ export default { }, ], }, + { + code: ` + /** + * + */ + interface GenericIdentityFn { + (arg: Type): Type; + } + `, + errors: [ + { + line: 2, + message: 'Missing @template Type', + }, + ], + languageOptions: { + parser: typescriptEslintParser + }, + }, + { + code: ` + /** + * + */ + export interface GenericIdentityFn { + (arg: Type): Type; + } + `, + errors: [ + { + line: 2, + message: 'Missing @template Type', + }, + ], + languageOptions: { + parser: typescriptEslintParser + }, + }, + { + code: ` + /** + * + */ + export default interface GenericIdentityFn { + (arg: Type): Type; + } + `, + errors: [ + { + line: 2, + message: 'Missing @template Type', + }, + ], + languageOptions: { + parser: typescriptEslintParser + }, + }, ], valid: [ { @@ -240,5 +297,44 @@ export default { */ `, }, + { + code: ` + /** + * @template Type + */ + interface GenericIdentityFn { + (arg: Type): Type; + } + `, + languageOptions: { + parser: typescriptEslintParser + }, + }, + { + code: ` + /** + * @template Type + */ + export interface GenericIdentityFn { + (arg: Type): Type; + } + `, + languageOptions: { + parser: typescriptEslintParser + }, + }, + { + code: ` + /** + * @template Type + */ + export default interface GenericIdentityFn { + (arg: Type): Type; + } + `, + languageOptions: { + parser: typescriptEslintParser + }, + }, ], };