Skip to content

Commit 7d082a2

Browse files
committed
fix(check-types): stop regression to unify *all* Object types including parent objects unless there is unifyParentAndChildTypeChecks config (should only unify with arrays); fixes gajus#800
Also updates devDeps.
1 parent 5530e07 commit 7d082a2

File tree

5 files changed

+281
-27
lines changed

5 files changed

+281
-27
lines changed

.README/rules/check-types.md

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ number
1313
bigint
1414
string
1515
symbol
16-
object
16+
object (For TypeScript's sake, however, using `Object` when specifying child types on it like `Object<string, number>`)
1717
Array
1818
Function
1919
Date
@@ -72,7 +72,7 @@ the `valid-types` rule to report parsing errors.
7272

7373
Why are `boolean`, `number` and `string` exempt from starting with a capital
7474
letter? Let's take `string` as an example. In Javascript, everything is an
75-
object. The string Object has prototypes for string functions such as
75+
object. The `String` object has prototypes for string functions such as
7676
`.toUpperCase()`.
7777

7878
Fortunately we don't have to write `new String()` everywhere in our code.
@@ -95,17 +95,50 @@ new String('lard') // String {0: "l", 1: "a", 2: "r", 3: "d", length: 4}
9595
new String('lard') === 'lard' // false
9696
```
9797

98-
To make things more confusing, there are also object literals and object
99-
Objects. But object literals are still static Objects and object Objects are
100-
instantiated Objects. So an object primitive is still an object Object.
98+
To make things more confusing, there are also object literals (like `{}`) and
99+
`Object` objects. But object literals are still static `Object`s and `Object`
100+
objects are instantiated objects. So an object primitive is still an `Object`
101+
object.
101102

102103
However, `Object.create(null)` objects are not `instanceof Object`, however, so
103-
in the case of this Object we lower-case to indicate possible support for
104-
these objects.
104+
in the case of such a plain object we lower-case to indicate possible support
105+
for these objects. Also, nowadays, TypeScript also discourages use of `Object`
106+
as a lone type. However, one additional complexity is that TypeScript allows and
107+
actually [currently requires](https://github.com/microsoft/TypeScript/issues/20555)
108+
`Object` (with the initial upper-case) if used in the syntax
109+
`Object.<keyType, valueType>` or `Object<keyType, valueType`, perhaps to
110+
adhere to what [JSDoc documents](https://jsdoc.app/tags-type.html).
111+
112+
So, for optimal compatibility with TypeScript (especially since TypeScript
113+
tools can be used on plain JavaScript with JSDoc), we are now enforcing this
114+
TypeScript approach as the default (without the dot) as well as disallowing
115+
`object.<>` or `object<>`, styles which TypeScript doesn't support in favor
116+
of `Object<>`, and disallowing plain `Object`—which [it discourages](https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html#general-types)
117+
([for](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#unions) [reasons](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#object-type)
118+
[similar](https://www.typescriptlang.org/docs/handbook/2/functions.html#object)
119+
to ours here)—in favor of `object`. (You might wish to use `preferredTypes` to
120+
prevent `Object<>` too, whether for simplicity and/or because of a general
121+
preference of the object shorthand TypeScript allows (e.g., `{prop: number}`).)
122+
Although earlier versions of TypeScript only worked with the dotted `Object.<>`
123+
form, and although the TypeScript docs currently use this on its [JSDoc page](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#type)
124+
as did [JSDoc](https://jsdoc.app/tags-type.html),
125+
the dot-less form has nevertheless been supported for some time in both
126+
environments, and seems to be favored by the community, so we are enforcing
127+
that now.
128+
129+
"preferredTypes": {
130+
// Use 'object' in typescript mode, see TypeScript's Do's and Dont's
131+
"Object": "object",
132+
"object.<>": "Object<>", // see https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/101
133+
"Object.<>": "Object<>",
134+
"object<>": "Object<>", // see https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/101
135+
},
136+
105137

106138
Basically, for primitives, we want to define the type as a primitive, because
107139
that's what we use in 99.9% of cases. For everything else, we use the type
108-
rather than the primitive. Otherwise it would all just be `{object}`.
140+
rather than the primitive. Otherwise it would all just be `{object}` (with the
141+
additional exception of the special case of `Object.<>` just mentioned).
109142

110143
In short: It's not about consistency, rather about the 99.9% use case. (And
111144
some functions might not even support the objects if they are checking for

README.md

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4728,7 +4728,7 @@ number
47284728
bigint
47294729
string
47304730
symbol
4731-
object
4731+
object (For TypeScript's sake, however, using `Object` when specifying child types on it like `Object<string, number>`)
47324732
Array
47334733
Function
47344734
Date
@@ -4789,7 +4789,7 @@ the `valid-types` rule to report parsing errors.
47894789

47904790
Why are `boolean`, `number` and `string` exempt from starting with a capital
47914791
letter? Let's take `string` as an example. In Javascript, everything is an
4792-
object. The string Object has prototypes for string functions such as
4792+
object. The `String` object has prototypes for string functions such as
47934793
`.toUpperCase()`.
47944794

47954795
Fortunately we don't have to write `new String()` everywhere in our code.
@@ -4812,17 +4812,50 @@ new String('lard') // String {0: "l", 1: "a", 2: "r", 3: "d", length: 4}
48124812
new String('lard') === 'lard' // false
48134813
```
48144814

4815-
To make things more confusing, there are also object literals and object
4816-
Objects. But object literals are still static Objects and object Objects are
4817-
instantiated Objects. So an object primitive is still an object Object.
4815+
To make things more confusing, there are also object literals (like `{}`) and
4816+
`Object` objects. But object literals are still static `Object`s and `Object`
4817+
objects are instantiated objects. So an object primitive is still an `Object`
4818+
object.
48184819

48194820
However, `Object.create(null)` objects are not `instanceof Object`, however, so
4820-
in the case of this Object we lower-case to indicate possible support for
4821-
these objects.
4821+
in the case of such a plain object we lower-case to indicate possible support
4822+
for these objects. Also, nowadays, TypeScript also discourages use of `Object`
4823+
as a lone type. However, one additional complexity is that TypeScript allows and
4824+
actually [currently requires](https://github.com/microsoft/TypeScript/issues/20555)
4825+
`Object` (with the initial upper-case) if used in the syntax
4826+
`Object.<keyType, valueType>` or `Object<keyType, valueType`, perhaps to
4827+
adhere to what [JSDoc documents](https://jsdoc.app/tags-type.html).
4828+
4829+
So, for optimal compatibility with TypeScript (especially since TypeScript
4830+
tools can be used on plain JavaScript with JSDoc), we are now enforcing this
4831+
TypeScript approach as the default (without the dot) as well as disallowing
4832+
`object.<>` or `object<>`, styles which TypeScript doesn't support in favor
4833+
of `Object<>`, and disallowing plain `Object`—which [it discourages](https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html#general-types)
4834+
([for](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#unions) [reasons](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#object-type)
4835+
[similar](https://www.typescriptlang.org/docs/handbook/2/functions.html#object)
4836+
to ours here)—in favor of `object`. (You might wish to use `preferredTypes` to
4837+
prevent `Object<>` too, whether for simplicity and/or because of a general
4838+
preference of the object shorthand TypeScript allows (e.g., `{prop: number}`).)
4839+
Although earlier versions of TypeScript only worked with the dotted `Object.<>`
4840+
form, and although the TypeScript docs currently use this on its [JSDoc page](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#type)
4841+
as did [JSDoc](https://jsdoc.app/tags-type.html),
4842+
the dot-less form has nevertheless been supported for some time in both
4843+
environments, and seems to be favored by the community, so we are enforcing
4844+
that now.
4845+
4846+
"preferredTypes": {
4847+
// Use 'object' in typescript mode, see TypeScript's Do's and Dont's
4848+
"Object": "object",
4849+
"object.<>": "Object<>", // see https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/101
4850+
"Object.<>": "Object<>",
4851+
"object<>": "Object<>", // see https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/101
4852+
},
4853+
48224854

48234855
Basically, for primitives, we want to define the type as a primitive, because
48244856
that's what we use in 99.9% of cases. For everything else, we use the type
4825-
rather than the primitive. Otherwise it would all just be `{object}`.
4857+
rather than the primitive. Otherwise it would all just be `{object}` (with the
4858+
additional exception of the special case of `Object.<>` just mentioned).
48264859

48274860
In short: It's not about consistency, rather about the 99.9% use case. (And
48284861
some functions might not even support the objects if they are checking for
@@ -5456,6 +5489,30 @@ function quux (foo) {
54565489
}
54575490
// Settings: {"jsdoc":{"mode":"typescript","preferredTypes":{"object.<>":"Object"}}}
54585491
// Message: Invalid JSDoc @param "foo" type "object"; prefer: "Object".
5492+
5493+
/**
5494+
* @param {object.<string, number>} foo
5495+
*/
5496+
function quux (foo) {
5497+
}
5498+
// Settings: {"jsdoc":{"mode":"typescript","preferredTypes":{"Object":"object","object.<>":"Object<>","Object.<>":"Object<>","object<>":"Object<>"}}}
5499+
// Message: Invalid JSDoc @param "foo" type "object"; prefer: "Object<>".
5500+
5501+
/**
5502+
* @param {Object.<string, number>} foo
5503+
*/
5504+
function quux (foo) {
5505+
}
5506+
// Settings: {"jsdoc":{"mode":"typescript","preferredTypes":{"Object":"object","object.<>":"Object<>","Object.<>":"Object<>","object<>":"Object<>"}}}
5507+
// Message: Invalid JSDoc @param "foo" type "Object"; prefer: "Object<>".
5508+
5509+
/**
5510+
* @param {object<string, number>} foo
5511+
*/
5512+
function quux (foo) {
5513+
}
5514+
// Settings: {"jsdoc":{"mode":"typescript","preferredTypes":{"Object":"object","object.<>":"Object<>","Object.<>":"Object<>","object<>":"Object<>"}}}
5515+
// Message: Invalid JSDoc @param "foo" type "object"; prefer: "Object<>".
54595516
````
54605517

54615518
The following patterns are not considered problems:
@@ -5751,6 +5808,18 @@ function quux (foo) {
57515808

57525809
}
57535810
// Settings: {"jsdoc":{"mode":"typescript"}}
5811+
5812+
/**
5813+
* @typedef {object} foo
5814+
*/
5815+
function a () {}
5816+
// Settings: {"jsdoc":{"mode":"typescript","preferredTypes":{"Object":"object","object.<>":"Object<>","object<>":"Object<>"}}}
5817+
5818+
/**
5819+
* @typedef {Object<string, number>} foo
5820+
*/
5821+
function a () {}
5822+
// Settings: {"jsdoc":{"mode":"typescript","preferredTypes":{"Object":"object","object.<>":"Object<>","object<>":"Object<>"}}}
57545823
````
57555824

57565825

package.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,28 @@
1616
},
1717
"description": "JSDoc linting rules for ESLint.",
1818
"devDependencies": {
19-
"@babel/cli": "^7.16.8",
20-
"@babel/core": "^7.16.12",
21-
"@babel/eslint-parser": "^7.16.5",
19+
"@babel/cli": "^7.17.0",
20+
"@babel/core": "^7.17.0",
21+
"@babel/eslint-parser": "^7.17.0",
2222
"@babel/node": "^7.16.8",
2323
"@babel/plugin-syntax-class-properties": "^7.12.13",
2424
"@babel/plugin-transform-flow-strip-types": "^7.16.7",
2525
"@babel/preset-env": "^7.16.11",
26-
"@babel/register": "^7.16.9",
26+
"@babel/register": "^7.17.0",
2727
"@hkdobrev/run-if-changed": "^0.3.1",
28-
"@typescript-eslint/parser": "^5.10.1",
28+
"@typescript-eslint/parser": "^5.10.2",
2929
"babel-plugin-add-module-exports": "^1.0.4",
3030
"babel-plugin-istanbul": "^6.1.1",
3131
"camelcase": "^6.3.0",
32-
"chai": "^4.3.5",
32+
"chai": "^4.3.6",
3333
"cross-env": "^7.0.3",
3434
"decamelize": "^5.0.1",
35-
"eslint": "^8.7.0",
35+
"eslint": "^8.8.0",
3636
"eslint-config-canonical": "^33.0.1",
3737
"gitdown": "^3.1.4",
3838
"glob": "^7.2.0",
3939
"husky": "^7.0.4",
40-
"lint-staged": "^12.3.1",
40+
"lint-staged": "^12.3.3",
4141
"lodash.defaultsdeep": "^4.6.1",
4242
"mocha": "^9.2.0",
4343
"nyc": "^15.1.0",

src/rules/checkTypes.js

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ export default iterateJsdoc(({
156156

157157
const tagName = jsdocTag.tag;
158158

159+
// eslint-disable-next-line complexity -- To refactor
159160
traverse(typeAst, (node, parentNode, property) => {
160161
const {
161162
type,
@@ -221,12 +222,28 @@ export default iterateJsdoc(({
221222
]);
222223
} else if (!noDefaults && type === 'JsdocTypeName') {
223224
for (const strictNativeType of strictNativeTypes) {
224-
if (strictNativeType === 'object' && mode === 'typescript' && !preferredTypes?.[nodeName]) {
225+
if (
226+
// Todo: Avoid typescript condition if moving to default typescript
227+
strictNativeType === 'object' && mode === 'typescript' &&
228+
(
229+
// This is not set to remap with exact type match (e.g.,
230+
// `object: 'Object'`), so can ignore (including if circular)
231+
!preferredTypes?.[nodeName] ||
232+
// Although present on `preferredTypes` for remapping, this is a
233+
// parent object without a parent match (and not
234+
// `unifyParentAndChildTypeChecks`) and we don't want
235+
// `object<>` given TypeScript issue https://github.com/microsoft/TypeScript/issues/20555
236+
parentNode?.elements.length && (
237+
parentNode?.left.type === 'JsdocTypeName' &&
238+
parentNode?.left.value === 'Object'
239+
)
240+
)
241+
) {
225242
continue;
226243
}
227244

228-
if (strictNativeType.toLowerCase() === nodeName.toLowerCase() &&
229-
strictNativeType !== nodeName &&
245+
if (strictNativeType !== nodeName &&
246+
strictNativeType.toLowerCase() === nodeName.toLowerCase() &&
230247

231248
// Don't report if user has own map for a strict native type
232249
(!preferredTypes || preferredTypes?.[strictNativeType] === undefined)

0 commit comments

Comments
 (0)