Skip to content

Commit 0f27282

Browse files
authored
feat: unless the user supplies their own object type preferredTypes, prefer object for plain objects and otherwise prefer Object<>; fixes #800 (#855)
1 parent a530862 commit 0f27282

File tree

4 files changed

+157
-57
lines changed

4 files changed

+157
-57
lines changed

.README/rules/check-types.md

+4-3
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,12 @@ as a lone type. However, one additional complexity is that TypeScript allows and
107107
actually [currently requires](https://github.com/microsoft/TypeScript/issues/20555)
108108
`Object` (with the initial upper-case) if used in the syntax
109109
`Object.<keyType, valueType>` or `Object<keyType, valueType`, perhaps to
110-
adhere to what [JSDoc documents](https://jsdoc.app/tags-type.html).
110+
adhere to that which [JSDoc documents](https://jsdoc.app/tags-type.html).
111111

112112
So, for optimal compatibility with TypeScript (especially since TypeScript
113-
tools can be used on plain JavaScript with JSDoc), we are now allowing this
114-
TypeScript approach by default.
113+
tools can be used on plain JavaScript with JSDoc), we are now requiring this
114+
TypeScript approach by default (if you set `object` type `preferredTypes` in
115+
TypeScript mode, the defaults will not apply).
115116

116117
Basically, for primitives, we want to define the type as a primitive, because
117118
that's what we use in 99.9% of cases. For everything else, we use the type

README.md

+25-6
Original file line numberDiff line numberDiff line change
@@ -4890,11 +4890,12 @@ as a lone type. However, one additional complexity is that TypeScript allows and
48904890
actually [currently requires](https://github.com/microsoft/TypeScript/issues/20555)
48914891
`Object` (with the initial upper-case) if used in the syntax
48924892
`Object.<keyType, valueType>` or `Object<keyType, valueType`, perhaps to
4893-
adhere to what [JSDoc documents](https://jsdoc.app/tags-type.html).
4893+
adhere to that which [JSDoc documents](https://jsdoc.app/tags-type.html).
48944894

48954895
So, for optimal compatibility with TypeScript (especially since TypeScript
4896-
tools can be used on plain JavaScript with JSDoc), we are now allowing this
4897-
TypeScript approach by default.
4896+
tools can be used on plain JavaScript with JSDoc), we are now requiring this
4897+
TypeScript approach by default (if you set `object` type `preferredTypes` in
4898+
TypeScript mode, the defaults will not apply).
48984899

48994900
Basically, for primitives, we want to define the type as a primitive, because
49004901
that's what we use in 99.9% of cases. For everything else, we use the type
@@ -5498,7 +5499,7 @@ function quux (foo) {
54985499
function a () {}
54995500

55005501
/**
5501-
* @typedef {Object} foo
5502+
* @typedef {Object<string>} foo
55025503
*/
55035504
function b () {}
55045505
// Settings: {"jsdoc":{"mode":"typescript","preferredTypes":{"object":"Object"}}}
@@ -5557,6 +5558,24 @@ function quux (foo) {
55575558
}
55585559
// Settings: {"jsdoc":{"mode":"typescript","preferredTypes":{"Object":"object","object.<>":"Object<>","Object.<>":"Object<>","object<>":"Object<>"}}}
55595560
// Message: Invalid JSDoc @param "foo" type "object"; prefer: "Object<>".
5561+
5562+
/**
5563+
* @param {object.<string>} foo
5564+
*/
5565+
function quux (foo) {
5566+
5567+
}
5568+
// Settings: {"jsdoc":{"mode":"typescript","preferredTypes":{"Object":"object","object.<>":"Object<>","Object.<>":"Object<>","object<>":"Object<>"}}}
5569+
// Message: Invalid JSDoc @param "foo" type "object"; prefer: "Object<>".
5570+
5571+
/**
5572+
* @param {object.<string>} foo
5573+
*/
5574+
function quux (foo) {
5575+
5576+
}
5577+
// Settings: {"jsdoc":{"mode":"typescript"}}
5578+
// Message: Invalid JSDoc @param "foo" type "object"; prefer: "Object<>".
55605579
````
55615580

55625581
The following patterns are not considered problems:
@@ -5817,7 +5836,7 @@ function b () {}
58175836
function a () {}
58185837

58195838
/**
5820-
* @typedef {Object} foo
5839+
* @typedef {Object<string>} foo
58215840
*/
58225841
function b () {}
58235842
// Settings: {"jsdoc":{"mode":"typescript"}}
@@ -5846,7 +5865,7 @@ function quux (foo) {
58465865
}
58475866

58485867
/**
5849-
* @param {Object.<string>} foo
5868+
* @param {Object<string>} foo
58505869
*/
58515870
function quux (foo) {
58525871

src/rules/checkTypes.js

+60-44
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,27 @@ export default iterateJsdoc(({
8686
});
8787

8888
const {
89-
preferredTypes,
89+
preferredTypes: preferredTypesOriginal,
9090
structuredTags,
9191
mode,
9292
} = settings;
93+
94+
const injectObjectPreferredTypes = !('Object' in preferredTypesOriginal ||
95+
'object' in preferredTypesOriginal ||
96+
'object.<>' in preferredTypesOriginal ||
97+
'Object.<>' in preferredTypesOriginal ||
98+
'object<>' in preferredTypesOriginal);
99+
100+
const preferredTypes = {
101+
...injectObjectPreferredTypes ? {
102+
Object: 'object',
103+
'object.<>': 'Object<>',
104+
'Object.<>': 'Object<>',
105+
'object<>': 'Object<>',
106+
} : {},
107+
...preferredTypesOriginal,
108+
};
109+
93110
const {
94111
noDefaults,
95112
unifyParentAndChildTypeChecks,
@@ -110,56 +127,55 @@ export default iterateJsdoc(({
110127
let hasMatchingPreferredType = false;
111128
let isGenericMatch = false;
112129
let typeName = typeNodeName;
113-
if (Object.keys(preferredTypes).length) {
114-
const isNameOfGeneric = parentNode !== undefined && parentNode.type === 'JsdocTypeGeneric' && property === 'left';
115-
if (unifyParentAndChildTypeChecks || isNameOfGeneric) {
116-
const brackets = parentNode?.meta?.brackets;
117-
const dot = parentNode?.meta?.dot;
118-
119-
if (brackets === 'angle') {
120-
const checkPostFixes = dot ? [
121-
'.', '.<>',
122-
] : [
123-
'<>',
124-
];
125-
isGenericMatch = checkPostFixes.some((checkPostFix) => {
126-
if (preferredTypes?.[typeNodeName + checkPostFix] !== undefined) {
127-
typeName += checkPostFix;
128-
129-
return true;
130-
}
131-
132-
return false;
133-
});
134-
}
135130

136-
if (!isGenericMatch && property) {
137-
const checkPostFixes = dot ? [
138-
'.', '.<>',
139-
] : [
140-
brackets === 'angle' ? '<>' : '[]',
141-
];
131+
const isNameOfGeneric = parentNode !== undefined && parentNode.type === 'JsdocTypeGeneric' && property === 'left';
132+
if (unifyParentAndChildTypeChecks || isNameOfGeneric) {
133+
const brackets = parentNode?.meta?.brackets;
134+
const dot = parentNode?.meta?.dot;
135+
136+
if (brackets === 'angle') {
137+
const checkPostFixes = dot ? [
138+
'.', '.<>',
139+
] : [
140+
'<>',
141+
];
142+
isGenericMatch = checkPostFixes.some((checkPostFix) => {
143+
if (preferredTypes?.[typeNodeName + checkPostFix] !== undefined) {
144+
typeName += checkPostFix;
145+
146+
return true;
147+
}
148+
149+
return false;
150+
});
151+
}
142152

143-
isGenericMatch = checkPostFixes.some((checkPostFix) => {
144-
if (preferredTypes?.[checkPostFix] !== undefined) {
145-
typeName = checkPostFix;
153+
if (!isGenericMatch && property) {
154+
const checkPostFixes = dot ? [
155+
'.', '.<>',
156+
] : [
157+
brackets === 'angle' ? '<>' : '[]',
158+
];
146159

147-
return true;
148-
}
160+
isGenericMatch = checkPostFixes.some((checkPostFix) => {
161+
if (preferredTypes?.[checkPostFix] !== undefined) {
162+
typeName = checkPostFix;
149163

150-
return false;
151-
});
152-
}
164+
return true;
165+
}
166+
167+
return false;
168+
});
153169
}
170+
}
154171

155-
const directNameMatch = preferredTypes?.[typeNodeName] !== undefined &&
156-
!Object.values(preferredTypes).includes(typeNodeName);
157-
const unifiedSyntaxParentMatch = property && directNameMatch && unifyParentAndChildTypeChecks;
158-
isGenericMatch = isGenericMatch || unifiedSyntaxParentMatch;
172+
const directNameMatch = preferredTypes?.[typeNodeName] !== undefined &&
173+
!Object.values(preferredTypes).includes(typeNodeName);
174+
const unifiedSyntaxParentMatch = property && directNameMatch && unifyParentAndChildTypeChecks;
175+
isGenericMatch = isGenericMatch || unifiedSyntaxParentMatch;
159176

160-
hasMatchingPreferredType = isGenericMatch ||
161-
directNameMatch && !property;
162-
}
177+
hasMatchingPreferredType = isGenericMatch ||
178+
directNameMatch && !property;
163179

164180
return [
165181
hasMatchingPreferredType, typeName, isGenericMatch,

test/rules/assertions/checkTypes.js

+68-4
Original file line numberDiff line numberDiff line change
@@ -2048,7 +2048,7 @@ export default {
20482048
function a () {}
20492049
20502050
/**
2051-
* @typedef {Object} foo
2051+
* @typedef {Object<string>} foo
20522052
*/
20532053
function b () {}
20542054
`,
@@ -2065,7 +2065,7 @@ export default {
20652065
function a () {}
20662066
20672067
/**
2068-
* @typedef {Object} foo
2068+
* @typedef {Object<string>} foo
20692069
*/
20702070
function b () {}
20712071
`,
@@ -2292,6 +2292,70 @@ export default {
22922292
},
22932293
},
22942294
},
2295+
{
2296+
code: `
2297+
/**
2298+
* @param {object.<string>} foo
2299+
*/
2300+
function quux (foo) {
2301+
2302+
}
2303+
`,
2304+
errors: [
2305+
{
2306+
line: 3,
2307+
message: 'Invalid JSDoc @param "foo" type "object"; prefer: "Object<>".',
2308+
},
2309+
],
2310+
output: `
2311+
/**
2312+
* @param {Object<string>} foo
2313+
*/
2314+
function quux (foo) {
2315+
2316+
}
2317+
`,
2318+
settings: {
2319+
jsdoc: {
2320+
mode: 'typescript',
2321+
preferredTypes: {
2322+
Object: 'object',
2323+
'object.<>': 'Object<>',
2324+
'Object.<>': 'Object<>',
2325+
'object<>': 'Object<>',
2326+
},
2327+
},
2328+
},
2329+
},
2330+
{
2331+
code: `
2332+
/**
2333+
* @param {object.<string>} foo
2334+
*/
2335+
function quux (foo) {
2336+
2337+
}
2338+
`,
2339+
errors: [
2340+
{
2341+
line: 3,
2342+
message: 'Invalid JSDoc @param "foo" type "object"; prefer: "Object<>".',
2343+
},
2344+
],
2345+
output: `
2346+
/**
2347+
* @param {Object<string>} foo
2348+
*/
2349+
function quux (foo) {
2350+
2351+
}
2352+
`,
2353+
settings: {
2354+
jsdoc: {
2355+
mode: 'typescript',
2356+
},
2357+
},
2358+
},
22952359
],
22962360
valid: [
22972361
{
@@ -2815,7 +2879,7 @@ export default {
28152879
function a () {}
28162880
28172881
/**
2818-
* @typedef {Object} foo
2882+
* @typedef {Object<string>} foo
28192883
*/
28202884
function b () {}
28212885
`,
@@ -2891,7 +2955,7 @@ export default {
28912955
{
28922956
code: `
28932957
/**
2894-
* @param {Object.<string>} foo
2958+
* @param {Object<string>} foo
28952959
*/
28962960
function quux (foo) {
28972961

0 commit comments

Comments
 (0)