diff --git a/.README/rules/require-template.md b/.README/rules/require-template.md index f90cad4c..a76b30ef 100644 --- a/.README/rules/require-template.md +++ b/.README/rules/require-template.md @@ -20,8 +20,8 @@ or Note that in the latter TypeScript-flavor JavaScript example, there is no way for us to firmly distinguish between `D` and `V` as type parameters or as some -other identifiers, so we use an algorithm that any single capital letters -are assumed to be templates. +other identifiers, so we use an algorithm that assumes that any single capital +letters are templates. ## Options diff --git a/docs/rules/require-template.md b/docs/rules/require-template.md index 581270a5..df907df9 100644 --- a/docs/rules/require-template.md +++ b/docs/rules/require-template.md @@ -22,8 +22,8 @@ or Note that in the latter TypeScript-flavor JavaScript example, there is no way for us to firmly distinguish between `D` and `V` as type parameters or as some -other identifiers, so we use an algorithm that any single capital letters -are assumed to be templates. +other identifiers, so we use an algorithm that assumes that any single capital +letters are templates. @@ -199,6 +199,18 @@ export default class { add: (x: NumType, y: NumType) => NumType; } // Message: Missing @template NumType + +/** + * @callback + * @param {[D, V | undefined]} someParam + */ +// Message: Missing @template D + +/** + * @callback + * @returns {[D, V | undefined]} + */ +// Message: Missing @template D ```` @@ -323,5 +335,24 @@ export default class { zeroValue: NumType; add: (x: NumType, y: NumType) => NumType; } + +/** + * @callback + * @template D + * @template V + * @param {[D, V | undefined]} someParam + */ + +/** + * @callback + * @template D + * @template V + * @returns {[D, V | undefined]} + */ + +/** + * @callback + * @returns {[Something | undefined]} + */ ```` diff --git a/src/rules/requireTemplate.js b/src/rules/requireTemplate.js index 8cdf8a59..7e01c247 100644 --- a/src/rules/requireTemplate.js +++ b/src/rules/requireTemplate.js @@ -89,12 +89,6 @@ export default iterateJsdoc(({ } }; - const typedefTags = utils.getTags('typedef'); - if (!typedefTags.length || typedefTags.length >= 2) { - handleTypes(); - return; - } - const usedNameToTag = new Map(); /** @@ -124,23 +118,45 @@ export default iterateJsdoc(({ }); }; - const potentialTypedef = typedefTags[0]; - checkForUsedTypes(potentialTypedef); + /** + * @param {string[]} tagNames + */ + const checkTagsAndTemplates = (tagNames) => { + for (const tagName of tagNames) { + const preferredTagName = /** @type {string} */ (utils.getPreferredTagName({ + tagName, + })); + const matchingTags = utils.getTags(preferredTagName); + for (const matchingTag of matchingTags) { + checkForUsedTypes(matchingTag); + } + } + + // Could check against whitelist/blacklist + for (const usedName of usedNames) { + if (!templateNames.includes(usedName)) { + report(`Missing @template ${usedName}`, null, usedNameToTag.get(usedName)); + } + } + }; - const tagName = /** @type {string} */ (utils.getPreferredTagName({ - tagName: 'property', - })); - const propertyTags = utils.getTags(tagName); - for (const propertyTag of propertyTags) { - checkForUsedTypes(propertyTag); + const callbackTags = utils.getTags('callback'); + const functionTags = utils.getTags('function'); + if (callbackTags.length || functionTags.length) { + checkTagsAndTemplates(['param', 'returns']); + return; } - // Could check against whitelist/blacklist - for (const usedName of usedNames) { - if (!templateNames.includes(usedName)) { - report(`Missing @template ${usedName}`, null, usedNameToTag.get(usedName)); - } + const typedefTags = utils.getTags('typedef'); + if (!typedefTags.length || typedefTags.length >= 2) { + handleTypes(); + return; } + + const potentialTypedef = typedefTags[0]; + checkForUsedTypes(potentialTypedef); + + checkTagsAndTemplates(['property']); }, { iterateAllJsdocs: true, meta: { diff --git a/test/rules/assertions/requireTemplate.js b/test/rules/assertions/requireTemplate.js index c2f713a2..ccfb6a36 100644 --- a/test/rules/assertions/requireTemplate.js +++ b/test/rules/assertions/requireTemplate.js @@ -365,6 +365,42 @@ export default { parser: typescriptEslintParser }, }, + { + code: ` + /** + * @callback + * @param {[D, V | undefined]} someParam + */ + `, + errors: [ + { + line: 4, + message: 'Missing @template D', + }, + { + line: 4, + message: 'Missing @template V', + }, + ], + }, + { + code: ` + /** + * @callback + * @returns {[D, V | undefined]} + */ + `, + errors: [ + { + line: 4, + message: 'Missing @template D', + }, + { + line: 4, + message: 'Missing @template V', + }, + ], + }, ], valid: [ { @@ -568,5 +604,33 @@ export default { parser: typescriptEslintParser }, }, + { + code: ` + /** + * @callback + * @template D + * @template V + * @param {[D, V | undefined]} someParam + */ + `, + }, + { + code: ` + /** + * @callback + * @template D + * @template V + * @returns {[D, V | undefined]} + */ + `, + }, + { + code: ` + /** + * @callback + * @returns {[Something | undefined]} + */ + `, + }, ], };