Skip to content

Commit

Permalink
Fix false positives for vars inside type in vue/valid-define-emits an…
Browse files Browse the repository at this point in the history
…d vue/valid-define-props rules (#1658)
  • Loading branch information
ota-meshi committed Oct 18, 2021
1 parent 00c3b99 commit 928e0c6
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 39 deletions.
3 changes: 3 additions & 0 deletions lib/rules/valid-define-emits.js
Expand Up @@ -85,6 +85,9 @@ module.exports = {
!utils.inRange(defineEmits.range, def.name)
)
) {
if (utils.withinTypeNode(node)) {
continue
}
//`defineEmits` are referencing locally declared variables.
context.report({
node,
Expand Down
3 changes: 3 additions & 0 deletions lib/rules/valid-define-props.js
Expand Up @@ -86,6 +86,9 @@ module.exports = {
!utils.inRange(defineProps.range, def.name)
)
) {
if (utils.withinTypeNode(node)) {
continue
}
//`defineProps` are referencing locally declared variables.
context.report({
node,
Expand Down
40 changes: 2 additions & 38 deletions lib/utils/indent-ts.js
Expand Up @@ -12,6 +12,7 @@ const {
isClosingBracketToken,
isOpeningBracketToken
} = require('eslint-utils')
const { isTypeNode } = require('./ts-ast-utils')

/**
* @typedef {import('../../typings/eslint-plugin-vue/util-types/indent-helper').TSNodeListener} TSNodeListener
Expand Down Expand Up @@ -224,46 +225,9 @@ function defineVisitor({
*/
// eslint-disable-next-line complexity -- ignore
'*[type=/^TS/]'(node) {
if (
node.type !== 'TSAnyKeyword' &&
node.type !== 'TSArrayType' &&
node.type !== 'TSBigIntKeyword' &&
node.type !== 'TSBooleanKeyword' &&
node.type !== 'TSConditionalType' &&
node.type !== 'TSConstructorType' &&
node.type !== 'TSFunctionType' &&
node.type !== 'TSImportType' &&
node.type !== 'TSIndexedAccessType' &&
node.type !== 'TSInferType' &&
node.type !== 'TSIntersectionType' &&
node.type !== 'TSIntrinsicKeyword' &&
node.type !== 'TSLiteralType' &&
node.type !== 'TSMappedType' &&
node.type !== 'TSNamedTupleMember' &&
node.type !== 'TSNeverKeyword' &&
node.type !== 'TSNullKeyword' &&
node.type !== 'TSNumberKeyword' &&
node.type !== 'TSObjectKeyword' &&
node.type !== 'TSOptionalType' &&
node.type !== 'TSRestType' &&
node.type !== 'TSStringKeyword' &&
node.type !== 'TSSymbolKeyword' &&
node.type !== 'TSTemplateLiteralType' &&
node.type !== 'TSThisType' &&
node.type !== 'TSTupleType' &&
node.type !== 'TSTypeLiteral' &&
node.type !== 'TSTypeOperator' &&
node.type !== 'TSTypePredicate' &&
node.type !== 'TSTypeQuery' &&
node.type !== 'TSTypeReference' &&
node.type !== 'TSUndefinedKeyword' &&
node.type !== 'TSUnionType' &&
node.type !== 'TSUnknownKeyword' &&
node.type !== 'TSVoidKeyword'
) {
if (!isTypeNode(node)) {
return
}
/** @type {TypeNode} */
const typeNode = node
if (/** @type {any} */ (typeNode.parent).type === 'TSParenthesizedType') {
return
Expand Down
24 changes: 23 additions & 1 deletion lib/utils/index.js
Expand Up @@ -54,7 +54,8 @@ const { traverseNodes, getFallbackKeys } = vueEslintParser.AST
const { findVariable } = require('eslint-utils')
const {
getComponentPropsFromTypeDefine,
getComponentEmitsFromTypeDefine
getComponentEmitsFromTypeDefine,
isTypeNode
} = require('./ts-ast-utils')

/**
Expand Down Expand Up @@ -1717,6 +1718,10 @@ module.exports = {
* Retrieve `ChainExpression#expression` value if the given node a `ChainExpression` node. Otherwise, pass through it.
*/
skipChainExpression,
/**
* Checks whether the given node is in a type annotation.
*/
withinTypeNode,
findVariableByIdentifier,
getScope,
/**
Expand Down Expand Up @@ -2226,6 +2231,23 @@ function skipChainExpression(node) {
return node
}

/**
* Checks whether the given node is in a type annotation.
* @param {ESNode} node
* @returns {boolean}
*/
function withinTypeNode(node) {
/** @type {ASTNode | null} */
let target = node
while (target) {
if (isTypeNode(target)) {
return true
}
target = target.parent
}
return false
}

/**
* Gets the property name of a given node.
* @param {Property|AssignmentProperty|MethodDefinition|MemberExpression} node - The node to get.
Expand Down
46 changes: 46 additions & 0 deletions lib/utils/ts-ast-utils.js
Expand Up @@ -4,6 +4,7 @@ const { findVariable } = require('eslint-utils')
* @typedef {import('@typescript-eslint/types').TSESTree.TSInterfaceBody} TSInterfaceBody
* @typedef {import('@typescript-eslint/types').TSESTree.TSTypeLiteral} TSTypeLiteral
* @typedef {import('@typescript-eslint/types').TSESTree.Parameter} TSESTreeParameter
* @typedef {import('@typescript-eslint/types').TSESTree.Node} Node
*
*/
/**
Expand All @@ -12,10 +13,55 @@ const { findVariable } = require('eslint-utils')
*/

module.exports = {
isTypeNode,
getComponentPropsFromTypeDefine,
getComponentEmitsFromTypeDefine
}

/**
* @param {Node | ASTNode} node
* @returns {node is TypeNode}
*/
function isTypeNode(node) {
return (
node.type === 'TSAnyKeyword' ||
node.type === 'TSArrayType' ||
node.type === 'TSBigIntKeyword' ||
node.type === 'TSBooleanKeyword' ||
node.type === 'TSConditionalType' ||
node.type === 'TSConstructorType' ||
node.type === 'TSFunctionType' ||
node.type === 'TSImportType' ||
node.type === 'TSIndexedAccessType' ||
node.type === 'TSInferType' ||
node.type === 'TSIntersectionType' ||
node.type === 'TSIntrinsicKeyword' ||
node.type === 'TSLiteralType' ||
node.type === 'TSMappedType' ||
node.type === 'TSNamedTupleMember' ||
node.type === 'TSNeverKeyword' ||
node.type === 'TSNullKeyword' ||
node.type === 'TSNumberKeyword' ||
node.type === 'TSObjectKeyword' ||
node.type === 'TSOptionalType' ||
node.type === 'TSRestType' ||
node.type === 'TSStringKeyword' ||
node.type === 'TSSymbolKeyword' ||
node.type === 'TSTemplateLiteralType' ||
node.type === 'TSThisType' ||
node.type === 'TSTupleType' ||
node.type === 'TSTypeLiteral' ||
node.type === 'TSTypeOperator' ||
node.type === 'TSTypePredicate' ||
node.type === 'TSTypeQuery' ||
node.type === 'TSTypeReference' ||
node.type === 'TSUndefinedKeyword' ||
node.type === 'TSUnionType' ||
node.type === 'TSUnknownKeyword' ||
node.type === 'TSVoidKeyword'
)
}

/**
* @param {TypeNode} node
* @returns {node is TSTypeLiteral}
Expand Down
44 changes: 44 additions & 0 deletions tests/lib/rules/valid-define-emits.js
Expand Up @@ -73,6 +73,50 @@ tester.run('valid-define-emits', rule, {
})
</script>
`
},
{
// https://github.com/vuejs/eslint-plugin-vue/issues/1656
filename: 'test.vue',
parserOptions: {
parser: require.resolve('@typescript-eslint/parser')
},
code: `
<script setup lang="ts">
import type { PropType } from 'vue';
type X = string;
const props = defineProps({
myProp: Array as PropType<string[]>,
});
const emit = defineEmits({
myProp: (x: X) => true,
});
</script>
`
},
{
filename: 'test.vue',
parserOptions: {
parser: require.resolve('@typescript-eslint/parser')
},
code: `
<script setup lang="ts">
import type { PropType } from 'vue';
const strList = ['a', 'b', 'c']
const str = 'abc'
const props = defineProps({
myProp: Array as PropType<typeof strList>,
});
const emit = defineEmits({
myProp: (x: typeof str) => true,
});
</script>
`
}
],
invalid: [
Expand Down
44 changes: 44 additions & 0 deletions tests/lib/rules/valid-define-props.js
Expand Up @@ -76,6 +76,50 @@ tester.run('valid-define-props', rule, {
})
</script>
`
},
{
// https://github.com/vuejs/eslint-plugin-vue/issues/1656
filename: 'test.vue',
parserOptions: {
parser: require.resolve('@typescript-eslint/parser')
},
code: `
<script setup lang="ts">
import type { PropType } from 'vue';
type X = string;
const props = defineProps({
myProp: Array as PropType<string[]>,
});
const emit = defineEmits({
myProp: (x: X) => true,
});
</script>
`
},
{
filename: 'test.vue',
parserOptions: {
parser: require.resolve('@typescript-eslint/parser')
},
code: `
<script setup lang="ts">
import type { PropType } from 'vue';
const strList = ['a', 'b', 'c']
const str = 'abc'
const props = defineProps({
myProp: Array as PropType<typeof strList>,
});
const emit = defineEmits({
myProp: (x: typeof str) => true,
});
</script>
`
}
],
invalid: [
Expand Down

0 comments on commit 928e0c6

Please sign in to comment.