Skip to content

Commit 5d99663

Browse files
committed
feat(check-types): add option exemptTagContexts to exempt type-checking (certain types or any types) on specific tags; fixes #255
1 parent f70fd6c commit 5d99663

File tree

4 files changed

+247
-3
lines changed

4 files changed

+247
-3
lines changed

.README/rules/check-types.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,18 @@ RegExp
2727
- with the key `noDefaults` to insist that only the supplied option type
2828
map is to be used, and that the default preferences (such as "string"
2929
over "String") will not be enforced. The option's default is `false`.
30+
- with the key `exemptTagContexts` which will avoid reporting when a
31+
bad type is found on a specified tag. Set to an array of objects with
32+
a key `tag` set to the tag to exempt, and a `types` key which can
33+
either be `true` to indicate that any types on that tag will be allowed,
34+
or to an array of strings which will only allow specific bad types.
35+
If an array of strings is given, these must match the type exactly,
36+
e.g., if you only allow `"object"`, it will not allow
37+
`"object<string, string>"`. Note that this is different from the
38+
behavior of `settings.jsdoc.preferredTypes`. This option is useful
39+
for normally restricting generic types like `object` with
40+
`preferredTypes`, but allowing `typedef` to indicate that its base
41+
type is `object`.
3042
- with the key `unifyParentAndChildTypeChecks` which will treat
3143
`settings.jsdoc.preferredTypes` keys such as `SomeType` as matching
3244
not only child types such as an unadorned `SomeType` but also

README.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2525,6 +2525,18 @@ RegExp
25252525
- with the key `noDefaults` to insist that only the supplied option type
25262526
map is to be used, and that the default preferences (such as "string"
25272527
over "String") will not be enforced. The option's default is `false`.
2528+
- with the key `exemptTagContexts` which will avoid reporting when a
2529+
bad type is found on a specified tag. Set to an array of objects with
2530+
a key `tag` set to the tag to exempt, and a `types` key which can
2531+
either be `true` to indicate that any types on that tag will be allowed,
2532+
or to an array of strings which will only allow specific bad types.
2533+
If an array of strings is given, these must match the type exactly,
2534+
e.g., if you only allow `"object"`, it will not allow
2535+
`"object<string, string>"`. Note that this is different from the
2536+
behavior of `settings.jsdoc.preferredTypes`. This option is useful
2537+
for normally restricting generic types like `object` with
2538+
`preferredTypes`, but allowing `typedef` to indicate that its base
2539+
type is `object`.
25282540
- with the key `unifyParentAndChildTypeChecks` which will treat
25292541
`settings.jsdoc.preferredTypes` keys such as `SomeType` as matching
25302542
not only child types such as an unadorned `SomeType` but also
@@ -3115,6 +3127,32 @@ function quux () {}
31153127
function quux () {}
31163128
// Settings: {"jsdoc":{"mode":"closure"}}
31173129
// Message: Invalid JSDoc @export type "array"; prefer: "Array".
3130+
3131+
/**
3132+
* @typedef {object} foo
3133+
* @property {object} bar
3134+
*/
3135+
// Settings: {"jsdoc":{"preferredTypes":{"object":"Object"}}}
3136+
// Options: [{"exemptTagContexts":[{"tag":"typedef","types":true}]}]
3137+
// Message: Invalid JSDoc @property "bar" type "object"; prefer: "Object".
3138+
3139+
/** @typedef {object} foo */
3140+
// Settings: {"jsdoc":{"preferredTypes":{"object":"Object"}}}
3141+
// Options: [{"exemptTagContexts":[{"tag":"typedef","types":["array"]}]}]
3142+
// Message: Invalid JSDoc @typedef "foo" type "object"; prefer: "Object".
3143+
3144+
/**
3145+
* @typedef {object} foo
3146+
* @property {object} bar
3147+
*/
3148+
// Settings: {"jsdoc":{"preferredTypes":{"object":"Object"}}}
3149+
// Options: [{"exemptTagContexts":[{"tag":"typedef","types":["object"]}]}]
3150+
// Message: Invalid JSDoc @property "bar" type "object"; prefer: "Object".
3151+
3152+
/** @typedef {object<string, string>} foo */
3153+
// Settings: {"jsdoc":{"preferredTypes":{"object<>":"Object<>"}}}
3154+
// Options: [{"exemptTagContexts":[{"tag":"typedef","types":["object"]}]}]
3155+
// Message: Invalid JSDoc @typedef "foo" type "object"; prefer: "Object<>".
31183156
````
31193157

31203158
The following patterns are not considered problems:
@@ -3337,6 +3375,17 @@ function quux () {}
33373375
// Settings: {"jsdoc":{"mode":"closure"}}
33383376

33393377
/** @type {new() => EntityBase} */
3378+
3379+
/** @typedef {object} foo */
3380+
// Settings: {"jsdoc":{"preferredTypes":{"object":"Object"}}}
3381+
// Options: [{"exemptTagContexts":[{"tag":"typedef","types":true}]}]
3382+
3383+
/** @typedef {object<string, string>} foo */
3384+
// Settings: {"jsdoc":{"preferredTypes":{"object":"Object"}}}
3385+
3386+
/** @typedef {object<string, string>} foo */
3387+
// Settings: {"jsdoc":{"preferredTypes":{"object<>":"Object<>"}}}
3388+
// Options: [{"exemptTagContexts":[{"tag":"typedef","types":["object<string, string>"]}]}]
33403389
````
33413390

33423391

src/rules/checkTypes.js

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,11 @@ export default iterateJsdoc(({
6060
});
6161

6262
const {preferredTypes} = settings;
63-
const optionObj = context.options[0];
64-
const noDefaults = _.get(optionObj, 'noDefaults');
65-
const unifyParentAndChildTypeChecks = _.get(optionObj, 'unifyParentAndChildTypeChecks');
63+
const {
64+
noDefaults,
65+
unifyParentAndChildTypeChecks,
66+
exemptTagContexts = [],
67+
} = context.options[0] || {};
6668

6769
const getPreferredTypeInfo = (type, nodeName, parentName, parentNode) => {
6870
let hasMatchingPreferredType;
@@ -197,6 +199,12 @@ export default iterateJsdoc(({
197199
};
198200

199201
const tagValue = jsdocTag.name ? ` "${jsdocTag.name}"` : '';
202+
if (exemptTagContexts.some(({tag, types}) => {
203+
return tag === tagName &&
204+
(types === true || types.includes(jsdocTag.type));
205+
})) {
206+
return;
207+
}
200208

201209
report(
202210
message ||
@@ -221,6 +229,31 @@ export default iterateJsdoc(({
221229
{
222230
additionalProperties: false,
223231
properties: {
232+
exemptTagContexts: {
233+
items: {
234+
additionalProperties: false,
235+
properties: {
236+
tag: {
237+
type: 'string',
238+
},
239+
types: {
240+
oneOf: [
241+
{
242+
type: 'boolean',
243+
},
244+
{
245+
items: {
246+
type: 'string',
247+
},
248+
type: 'array',
249+
},
250+
],
251+
},
252+
},
253+
type: 'object',
254+
},
255+
type: 'array',
256+
},
224257
noDefaults: {
225258
type: 'boolean',
226259
},

test/rules/assertions/checkTypes.js

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1737,6 +1737,110 @@ export default {
17371737
},
17381738
},
17391739
},
1740+
{
1741+
code: `
1742+
/**
1743+
* @typedef {object} foo
1744+
* @property {object} bar
1745+
*/
1746+
`,
1747+
errors: [
1748+
{
1749+
line: 4,
1750+
message: 'Invalid JSDoc @property "bar" type "object"; prefer: "Object".',
1751+
},
1752+
],
1753+
options: [
1754+
{
1755+
exemptTagContexts: [{
1756+
tag: 'typedef',
1757+
types: true,
1758+
}],
1759+
},
1760+
],
1761+
settings: {
1762+
jsdoc: {
1763+
preferredTypes: {
1764+
object: 'Object',
1765+
},
1766+
},
1767+
},
1768+
},
1769+
{
1770+
code: '/** @typedef {object} foo */',
1771+
errors: [
1772+
{
1773+
line: 1,
1774+
message: 'Invalid JSDoc @typedef "foo" type "object"; prefer: "Object".',
1775+
},
1776+
],
1777+
options: [
1778+
{
1779+
exemptTagContexts: [{
1780+
tag: 'typedef',
1781+
types: ['array'],
1782+
}],
1783+
},
1784+
],
1785+
settings: {
1786+
jsdoc: {
1787+
preferredTypes: {
1788+
object: 'Object',
1789+
},
1790+
},
1791+
},
1792+
},
1793+
{
1794+
code: `
1795+
/**
1796+
* @typedef {object} foo
1797+
* @property {object} bar
1798+
*/
1799+
`,
1800+
errors: [
1801+
{
1802+
line: 4,
1803+
message: 'Invalid JSDoc @property "bar" type "object"; prefer: "Object".',
1804+
},
1805+
],
1806+
options: [
1807+
{
1808+
exemptTagContexts: [{
1809+
tag: 'typedef',
1810+
types: ['object'],
1811+
}],
1812+
},
1813+
],
1814+
settings: {
1815+
jsdoc: {
1816+
preferredTypes: {
1817+
object: 'Object',
1818+
},
1819+
},
1820+
},
1821+
},
1822+
{
1823+
code: '/** @typedef {object<string, string>} foo */',
1824+
errors: [{
1825+
line: 1,
1826+
message: 'Invalid JSDoc @typedef "foo" type "object"; prefer: "Object<>".',
1827+
}],
1828+
options: [
1829+
{
1830+
exemptTagContexts: [{
1831+
tag: 'typedef',
1832+
types: ['object'],
1833+
}],
1834+
},
1835+
],
1836+
settings: {
1837+
jsdoc: {
1838+
preferredTypes: {
1839+
'object<>': 'Object<>',
1840+
},
1841+
},
1842+
},
1843+
},
17401844
],
17411845
valid: [
17421846
{
@@ -2154,5 +2258,51 @@ export default {
21542258
/** @type {new() => EntityBase} */
21552259
`,
21562260
},
2261+
{
2262+
code: '/** @typedef {object} foo */',
2263+
options: [
2264+
{
2265+
exemptTagContexts: [{
2266+
tag: 'typedef',
2267+
types: true,
2268+
}],
2269+
},
2270+
],
2271+
settings: {
2272+
jsdoc: {
2273+
preferredTypes: {
2274+
object: 'Object',
2275+
},
2276+
},
2277+
},
2278+
},
2279+
{
2280+
code: '/** @typedef {object<string, string>} foo */',
2281+
settings: {
2282+
jsdoc: {
2283+
preferredTypes: {
2284+
object: 'Object',
2285+
},
2286+
},
2287+
},
2288+
},
2289+
{
2290+
code: '/** @typedef {object<string, string>} foo */',
2291+
options: [
2292+
{
2293+
exemptTagContexts: [{
2294+
tag: 'typedef',
2295+
types: ['object<string, string>'],
2296+
}],
2297+
},
2298+
],
2299+
settings: {
2300+
jsdoc: {
2301+
preferredTypes: {
2302+
'object<>': 'Object<>',
2303+
},
2304+
},
2305+
},
2306+
},
21572307
],
21582308
};

0 commit comments

Comments
 (0)