Skip to content

Commit

Permalink
feat(check-types): add option exemptTagContexts to exempt type-ch…
Browse files Browse the repository at this point in the history
…ecking (certain types or any types) on specific tags; fixes #255
  • Loading branch information
brettz9 committed Dec 31, 2019
1 parent f70fd6c commit 5d99663
Show file tree
Hide file tree
Showing 4 changed files with 247 additions and 3 deletions.
12 changes: 12 additions & 0 deletions .README/rules/check-types.md
Expand Up @@ -27,6 +27,18 @@ RegExp
- with the key `noDefaults` to insist that only the supplied option type
map is to be used, and that the default preferences (such as "string"
over "String") will not be enforced. The option's default is `false`.
- with the key `exemptTagContexts` which will avoid reporting when a
bad type is found on a specified tag. Set to an array of objects with
a key `tag` set to the tag to exempt, and a `types` key which can
either be `true` to indicate that any types on that tag will be allowed,
or to an array of strings which will only allow specific bad types.
If an array of strings is given, these must match the type exactly,
e.g., if you only allow `"object"`, it will not allow
`"object<string, string>"`. Note that this is different from the
behavior of `settings.jsdoc.preferredTypes`. This option is useful
for normally restricting generic types like `object` with
`preferredTypes`, but allowing `typedef` to indicate that its base
type is `object`.
- with the key `unifyParentAndChildTypeChecks` which will treat
`settings.jsdoc.preferredTypes` keys such as `SomeType` as matching
not only child types such as an unadorned `SomeType` but also
Expand Down
49 changes: 49 additions & 0 deletions README.md
Expand Up @@ -2525,6 +2525,18 @@ RegExp
- with the key `noDefaults` to insist that only the supplied option type
map is to be used, and that the default preferences (such as "string"
over "String") will not be enforced. The option's default is `false`.
- with the key `exemptTagContexts` which will avoid reporting when a
bad type is found on a specified tag. Set to an array of objects with
a key `tag` set to the tag to exempt, and a `types` key which can
either be `true` to indicate that any types on that tag will be allowed,
or to an array of strings which will only allow specific bad types.
If an array of strings is given, these must match the type exactly,
e.g., if you only allow `"object"`, it will not allow
`"object<string, string>"`. Note that this is different from the
behavior of `settings.jsdoc.preferredTypes`. This option is useful
for normally restricting generic types like `object` with
`preferredTypes`, but allowing `typedef` to indicate that its base
type is `object`.
- with the key `unifyParentAndChildTypeChecks` which will treat
`settings.jsdoc.preferredTypes` keys such as `SomeType` as matching
not only child types such as an unadorned `SomeType` but also
Expand Down Expand Up @@ -3115,6 +3127,32 @@ function quux () {}
function quux () {}
// Settings: {"jsdoc":{"mode":"closure"}}
// Message: Invalid JSDoc @export type "array"; prefer: "Array".

/**
* @typedef {object} foo
* @property {object} bar
*/
// Settings: {"jsdoc":{"preferredTypes":{"object":"Object"}}}
// Options: [{"exemptTagContexts":[{"tag":"typedef","types":true}]}]
// Message: Invalid JSDoc @property "bar" type "object"; prefer: "Object".

/** @typedef {object} foo */
// Settings: {"jsdoc":{"preferredTypes":{"object":"Object"}}}
// Options: [{"exemptTagContexts":[{"tag":"typedef","types":["array"]}]}]
// Message: Invalid JSDoc @typedef "foo" type "object"; prefer: "Object".

/**
* @typedef {object} foo
* @property {object} bar
*/
// Settings: {"jsdoc":{"preferredTypes":{"object":"Object"}}}
// Options: [{"exemptTagContexts":[{"tag":"typedef","types":["object"]}]}]
// Message: Invalid JSDoc @property "bar" type "object"; prefer: "Object".

/** @typedef {object<string, string>} foo */
// Settings: {"jsdoc":{"preferredTypes":{"object<>":"Object<>"}}}
// Options: [{"exemptTagContexts":[{"tag":"typedef","types":["object"]}]}]
// Message: Invalid JSDoc @typedef "foo" type "object"; prefer: "Object<>".
````

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

/** @type {new() => EntityBase} */

/** @typedef {object} foo */
// Settings: {"jsdoc":{"preferredTypes":{"object":"Object"}}}
// Options: [{"exemptTagContexts":[{"tag":"typedef","types":true}]}]

/** @typedef {object<string, string>} foo */
// Settings: {"jsdoc":{"preferredTypes":{"object":"Object"}}}

/** @typedef {object<string, string>} foo */
// Settings: {"jsdoc":{"preferredTypes":{"object<>":"Object<>"}}}
// Options: [{"exemptTagContexts":[{"tag":"typedef","types":["object<string, string>"]}]}]
````


Expand Down
39 changes: 36 additions & 3 deletions src/rules/checkTypes.js
Expand Up @@ -60,9 +60,11 @@ export default iterateJsdoc(({
});

const {preferredTypes} = settings;
const optionObj = context.options[0];
const noDefaults = _.get(optionObj, 'noDefaults');
const unifyParentAndChildTypeChecks = _.get(optionObj, 'unifyParentAndChildTypeChecks');
const {
noDefaults,
unifyParentAndChildTypeChecks,
exemptTagContexts = [],
} = context.options[0] || {};

const getPreferredTypeInfo = (type, nodeName, parentName, parentNode) => {
let hasMatchingPreferredType;
Expand Down Expand Up @@ -197,6 +199,12 @@ export default iterateJsdoc(({
};

const tagValue = jsdocTag.name ? ` "${jsdocTag.name}"` : '';
if (exemptTagContexts.some(({tag, types}) => {
return tag === tagName &&
(types === true || types.includes(jsdocTag.type));
})) {
return;
}

report(
message ||
Expand All @@ -221,6 +229,31 @@ export default iterateJsdoc(({
{
additionalProperties: false,
properties: {
exemptTagContexts: {
items: {
additionalProperties: false,
properties: {
tag: {
type: 'string',
},
types: {
oneOf: [
{
type: 'boolean',
},
{
items: {
type: 'string',
},
type: 'array',
},
],
},
},
type: 'object',
},
type: 'array',
},
noDefaults: {
type: 'boolean',
},
Expand Down
150 changes: 150 additions & 0 deletions test/rules/assertions/checkTypes.js
Expand Up @@ -1737,6 +1737,110 @@ export default {
},
},
},
{
code: `
/**
* @typedef {object} foo
* @property {object} bar
*/
`,
errors: [
{
line: 4,
message: 'Invalid JSDoc @property "bar" type "object"; prefer: "Object".',
},
],
options: [
{
exemptTagContexts: [{
tag: 'typedef',
types: true,
}],
},
],
settings: {
jsdoc: {
preferredTypes: {
object: 'Object',
},
},
},
},
{
code: '/** @typedef {object} foo */',
errors: [
{
line: 1,
message: 'Invalid JSDoc @typedef "foo" type "object"; prefer: "Object".',
},
],
options: [
{
exemptTagContexts: [{
tag: 'typedef',
types: ['array'],
}],
},
],
settings: {
jsdoc: {
preferredTypes: {
object: 'Object',
},
},
},
},
{
code: `
/**
* @typedef {object} foo
* @property {object} bar
*/
`,
errors: [
{
line: 4,
message: 'Invalid JSDoc @property "bar" type "object"; prefer: "Object".',
},
],
options: [
{
exemptTagContexts: [{
tag: 'typedef',
types: ['object'],
}],
},
],
settings: {
jsdoc: {
preferredTypes: {
object: 'Object',
},
},
},
},
{
code: '/** @typedef {object<string, string>} foo */',
errors: [{
line: 1,
message: 'Invalid JSDoc @typedef "foo" type "object"; prefer: "Object<>".',
}],
options: [
{
exemptTagContexts: [{
tag: 'typedef',
types: ['object'],
}],
},
],
settings: {
jsdoc: {
preferredTypes: {
'object<>': 'Object<>',
},
},
},
},
],
valid: [
{
Expand Down Expand Up @@ -2154,5 +2258,51 @@ export default {
/** @type {new() => EntityBase} */
`,
},
{
code: '/** @typedef {object} foo */',
options: [
{
exemptTagContexts: [{
tag: 'typedef',
types: true,
}],
},
],
settings: {
jsdoc: {
preferredTypes: {
object: 'Object',
},
},
},
},
{
code: '/** @typedef {object<string, string>} foo */',
settings: {
jsdoc: {
preferredTypes: {
object: 'Object',
},
},
},
},
{
code: '/** @typedef {object<string, string>} foo */',
options: [
{
exemptTagContexts: [{
tag: 'typedef',
types: ['object<string, string>'],
}],
},
],
settings: {
jsdoc: {
preferredTypes: {
'object<>': 'Object<>',
},
},
},
},
],
};

0 comments on commit 5d99663

Please sign in to comment.