diff --git a/.README/README.md b/.README/README.md index 99ce95a27..9fbf83804 100644 --- a/.README/README.md +++ b/.README/README.md @@ -428,6 +428,7 @@ values are objects with the following optional properties: might use this with `@throws` to suggest that only free form text is being input or with `@augments` (for jsdoc mode) to disallow Closure-style bracketed usage along with a required namepath. + - (An array of strings) - A list of permissible types. - `required` - Array of one of the following (defaults to an empty array, meaning none are required): - One or both of the following strings (if both are included, then both diff --git a/.README/rules/check-types.md b/.README/rules/check-types.md index ef90a4fa2..65496ffbe 100644 --- a/.README/rules/check-types.md +++ b/.README/rules/check-types.md @@ -122,7 +122,8 @@ String | **string** | **string** | `("test") instanceof String` -> **`false`** If you define your own tags and don't wish their bracketed portions checked for types, you can use `settings.jsdoc.structuredTags` with a tag `type` of -`false`. +`false`. If you set their `type` to an array, only those values will be +permitted. ||| |---|---| diff --git a/README.md b/README.md index 1a4355f62..609f04c07 100644 --- a/README.md +++ b/README.md @@ -497,6 +497,7 @@ values are objects with the following optional properties: might use this with `@throws` to suggest that only free form text is being input or with `@augments` (for jsdoc mode) to disallow Closure-style bracketed usage along with a required namepath. + - (An array of strings) - A list of permissible types. - `required` - Array of one of the following (defaults to an empty array, meaning none are required): - One or both of the following strings (if both are included, then both @@ -4449,7 +4450,8 @@ String | **string** | **string** | `("test") instanceof String` -> **`false`** If you define your own tags and don't wish their bracketed portions checked for types, you can use `settings.jsdoc.structuredTags` with a tag `type` of -`false`. +`false`. If you set their `type` to an array, only those values will be +permitted. ||| |---|---| @@ -5039,6 +5041,12 @@ function b () {} */ // Settings: {"jsdoc":{"structuredTags":{"aCustomTag":{"type":true}}}} // Message: Invalid JSDoc @aCustomTag "foo" type "Number"; prefer: "number". + +/** + * @aCustomTag {Number} foo + */ +// Settings: {"jsdoc":{"structuredTags":{"aCustomTag":{"type":["otherType","anotherType"]}}}} +// Message: Invalid JSDoc @aCustomTag "foo" type "Number"; prefer: ["otherType","anotherType"]. ```` The following patterns are not considered problems: @@ -5308,6 +5316,16 @@ function b () {} * @aCustomTag {Number} foo */ // Settings: {"jsdoc":{"structuredTags":{"aCustomTag":{"type":false}}}} + +/** + * @aCustomTag {otherType} foo + */ +// Settings: {"jsdoc":{"structuredTags":{"aCustomTag":{"type":["otherType","anotherType"]}}}} + +/** + * @aCustomTag {anotherType|otherType} foo + */ +// Settings: {"jsdoc":{"structuredTags":{"aCustomTag":{"type":["otherType","anotherType"]}}}} ```` diff --git a/src/rules/checkTypes.js b/src/rules/checkTypes.js index e9d6db223..61300b16d 100644 --- a/src/rules/checkTypes.js +++ b/src/rules/checkTypes.js @@ -71,7 +71,7 @@ export default iterateJsdoc(({ return utils.tagMightHaveTypePosition(tag.tag); }); - const {preferredTypes, mode} = settings; + const {preferredTypes, structuredTags, mode} = settings; const { noDefaults, unifyParentAndChildTypeChecks, @@ -138,6 +138,7 @@ export default iterateJsdoc(({ } catch { return; } + const tagName = jsdocTag.tag; traverse(typeAst, (node, parentName, parentNode) => { const {type, name} = node; @@ -149,6 +150,7 @@ export default iterateJsdoc(({ const [hasMatchingPreferredType, typeName, isGenericMatch] = getPreferredTypeInfo(type, nodeName, parentName, parentNode); let preferred; + let types; if (hasMatchingPreferredType) { const preferredSetting = preferredTypes[typeName]; nodeName = typeName === '[]' ? typeName : nodeName; @@ -172,6 +174,14 @@ export default iterateJsdoc(({ return; } + } else if (Object.entries(structuredTags).some(([tag, {type: typs}]) => { + types = typs; + + return tag === tagName && + Array.isArray(types) && + !types.includes(nodeName); + })) { + invalidTypes.push([nodeName, types]); } else if (!noDefaults && type === 'NAME') { for (const strictNativeType of strictNativeTypes) { if (strictNativeType === 'object' && mode === 'typescript') { @@ -199,7 +209,6 @@ export default iterateJsdoc(({ if (invalidTypes.length) { const fixedType = publish(typeAst); - const tagName = jsdocTag.tag; invalidTypes.forEach(([badType, preferredType = '', message]) => { const fix = (fixer) => { return fixer.replaceText( @@ -223,7 +232,7 @@ export default iterateJsdoc(({ message || `Invalid JSDoc @${tagName}${tagValue} type "${badType}"` + (preferredType ? '; ' : '.') + - (preferredType ? `prefer: "${preferredType}".` : ''), + (preferredType ? `prefer: ${JSON.stringify(preferredType)}.` : ''), preferredType ? fix : null, jsdocTag, message ? { diff --git a/test/rules/assertions/checkTypes.js b/test/rules/assertions/checkTypes.js index daebad665..913c160f1 100644 --- a/test/rules/assertions/checkTypes.js +++ b/test/rules/assertions/checkTypes.js @@ -1989,6 +1989,28 @@ export default { }, }, }, + { + code: ` + /** + * @aCustomTag {Number} foo + */ + `, + errors: [ + { + line: 3, + message: 'Invalid JSDoc @aCustomTag "foo" type "Number"; prefer: ["otherType","anotherType"].', + }, + ], + settings: { + jsdoc: { + structuredTags: { + aCustomTag: { + type: ['otherType', 'anotherType'], + }, + }, + }, + }, + }, ], valid: [ { @@ -2526,5 +2548,37 @@ export default { }, }, }, + { + code: ` + /** + * @aCustomTag {otherType} foo + */ + `, + settings: { + jsdoc: { + structuredTags: { + aCustomTag: { + type: ['otherType', 'anotherType'], + }, + }, + }, + }, + }, + { + code: ` + /** + * @aCustomTag {anotherType|otherType} foo + */ + `, + settings: { + jsdoc: { + structuredTags: { + aCustomTag: { + type: ['otherType', 'anotherType'], + }, + }, + }, + }, + }, ], };