diff --git a/.README/rules/check-types.md b/.README/rules/check-types.md index 999f6fc64..c6cd0f319 100644 --- a/.README/rules/check-types.md +++ b/.README/rules/check-types.md @@ -103,7 +103,8 @@ object. However, `Object.create(null)` objects are not `instanceof Object`, however, so in the case of such a plain object we lower-case to indicate possible support -for these objects. Also, nowadays, TypeScript also discourages use of `Object` +for these objects. Also, nowadays, TypeScript also [discourages](https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html#:~:text=%E2%9D%8C%20Don't%20ever%20use,used%20appropriately%20in%20JavaScript%20code.) +use of `Object` as a lone type. However, one additional complexity is that TypeScript allows and actually [currently requires](https://github.com/microsoft/TypeScript/issues/20555) `Object` (with the initial upper-case) if used in the syntax @@ -112,8 +113,14 @@ adhere to that which [JSDoc documents](https://jsdoc.app/tags-type.html). So, for optimal compatibility with TypeScript (especially since TypeScript tools can be used on plain JavaScript with JSDoc), we are now requiring this -TypeScript approach by default (if you set `object` type `preferredTypes` in -TypeScript mode, the defaults will not apply). +TypeScript approach by default in non-"typescript" mode (if you set +`object` type `preferredTypes` in TypeScript mode, the defaults will +not apply). + +However, for "typescript" mode, a still better choice exists—using index signatures such as `{[key: string]: string}` or using a more precise +shorthand object syntax (e.g., `{a: string, b: number}`). This is superior +for TypeScript because the likes of `Object` is not useable +in native TypeScript syntax, even if it is allowed within JSDoc. Basically, for primitives, we want to define the type as a primitive, because that's what we use in 99.9% of cases. For everything else, we use the type diff --git a/README.md b/README.md index 3f684733a..a05e96407 100644 --- a/README.md +++ b/README.md @@ -5442,7 +5442,8 @@ object. However, `Object.create(null)` objects are not `instanceof Object`, however, so in the case of such a plain object we lower-case to indicate possible support -for these objects. Also, nowadays, TypeScript also discourages use of `Object` +for these objects. Also, nowadays, TypeScript also [discourages](https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html#:~:text=%E2%9D%8C%20Don't%20ever%20use,used%20appropriately%20in%20JavaScript%20code.) +use of `Object` as a lone type. However, one additional complexity is that TypeScript allows and actually [currently requires](https://github.com/microsoft/TypeScript/issues/20555) `Object` (with the initial upper-case) if used in the syntax @@ -5451,8 +5452,14 @@ adhere to that which [JSDoc documents](https://jsdoc.app/tags-type.html). So, for optimal compatibility with TypeScript (especially since TypeScript tools can be used on plain JavaScript with JSDoc), we are now requiring this -TypeScript approach by default (if you set `object` type `preferredTypes` in -TypeScript mode, the defaults will not apply). +TypeScript approach by default in non-"typescript" mode (if you set +`object` type `preferredTypes` in TypeScript mode, the defaults will +not apply). + +However, for "typescript" mode, a still better choice exists—using index signatures such as `{[key: string]: string}` or using a more precise +shorthand object syntax (e.g., `{a: string, b: number}`). This is superior +for TypeScript because the likes of `Object` is not useable +in native TypeScript syntax, even if it is allowed within JSDoc. Basically, for primitives, we want to define the type as a primitive, because that's what we use in 99.9% of cases. For everything else, we use the type @@ -6132,7 +6139,7 @@ function quux (foo) { } // Settings: {"jsdoc":{"mode":"typescript"}} -// Message: Invalid JSDoc @param "foo" type "object"; prefer: "Object<>". +// Message: Use object shorthand or index signatures instead of `object`, e.g., `{[key: string]: string}` /** * @@ -6414,7 +6421,7 @@ function b () {} function a () {} /** - * @typedef {Object} foo + * @typedef {{[key: string]: number}} foo */ function b () {} // Settings: {"jsdoc":{"mode":"typescript"}} @@ -6443,7 +6450,7 @@ function quux (foo) { } /** - * @param {Object} foo + * @param {{[key: string]: number}} foo */ function quux (foo) { diff --git a/src/rules/checkTypes.js b/src/rules/checkTypes.js index 746fd81b7..6778c5549 100644 --- a/src/rules/checkTypes.js +++ b/src/rules/checkTypes.js @@ -73,6 +73,11 @@ const adjustNames = (type, preferred, isGenericMatch, typeNodeName, node, parent } }; +const getMessage = (upperCase) => { + return 'Use object shorthand or index signatures instead of ' + + '`' + (upperCase ? 'O' : 'o') + 'bject`, e.g., `{[key: string]: string}`'; +}; + export default iterateJsdoc(({ jsdocNode, sourceCode, @@ -97,13 +102,35 @@ export default iterateJsdoc(({ 'Object.<>' in preferredTypesOriginal || 'object<>' in preferredTypesOriginal); - const preferredTypes = { - ...injectObjectPreferredTypes ? { + const info = { + message: getMessage(), + replacement: false, + }; + + const infoUC = { + message: getMessage(true), + replacement: false, + }; + + const typeToInject = mode === 'typescript' ? + { + Object: 'object', + 'object.<>': info, + 'Object.<>': infoUC, + 'object<>': info, + 'Object<>': infoUC, + } : + { Object: 'object', 'object.<>': 'Object<>', 'Object.<>': 'Object<>', 'object<>': 'Object<>', - } : {}, + }; + + const preferredTypes = { + ...injectObjectPreferredTypes ? + typeToInject : + {}, ...preferredTypesOriginal, }; @@ -364,7 +391,7 @@ export default iterateJsdoc(({ for (const [ badType, preferredType = '', - message, + msg, ] of invalidTypes) { const tagValue = jsdocTag.name ? ` "${jsdocTag.name}"` : ''; if (exemptTagContexts.some(({ @@ -378,13 +405,13 @@ export default iterateJsdoc(({ } report( - message || + msg || `Invalid JSDoc @${tagName}${tagValue} type "${badType}"` + (preferredType ? '; ' : '.') + (preferredType ? `prefer: ${JSON.stringify(preferredType)}.` : ''), preferredType ? fix : null, jsdocTag, - message ? { + msg ? { tagName, tagValue, } : null, diff --git a/test/rules/assertions/checkTypes.js b/test/rules/assertions/checkTypes.js index 67c45d163..33aa941af 100644 --- a/test/rules/assertions/checkTypes.js +++ b/test/rules/assertions/checkTypes.js @@ -2339,17 +2339,9 @@ export default { errors: [ { line: 3, - message: 'Invalid JSDoc @param "foo" type "object"; prefer: "Object<>".', + message: 'Use object shorthand or index signatures instead of `object`, e.g., `{[key: string]: string}`', }, ], - output: ` - /** - * @param {Object} foo - */ - function quux (foo) { - - } - `, settings: { jsdoc: { mode: 'typescript', @@ -2944,7 +2936,7 @@ export default { function a () {} /** - * @typedef {Object} foo + * @typedef {{[key: string]: number}} foo */ function b () {} `, @@ -3020,7 +3012,7 @@ export default { { code: ` /** - * @param {Object} foo + * @param {{[key: string]: number}} foo */ function quux (foo) {