Skip to content

Commit 928e0c6

Browse files
authored
Fix false positives for vars inside type in vue/valid-define-emits and vue/valid-define-props rules (#1658)
1 parent 00c3b99 commit 928e0c6

File tree

7 files changed

+165
-39
lines changed

7 files changed

+165
-39
lines changed

lib/rules/valid-define-emits.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ module.exports = {
8585
!utils.inRange(defineEmits.range, def.name)
8686
)
8787
) {
88+
if (utils.withinTypeNode(node)) {
89+
continue
90+
}
8891
//`defineEmits` are referencing locally declared variables.
8992
context.report({
9093
node,

lib/rules/valid-define-props.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ module.exports = {
8686
!utils.inRange(defineProps.range, def.name)
8787
)
8888
) {
89+
if (utils.withinTypeNode(node)) {
90+
continue
91+
}
8992
//`defineProps` are referencing locally declared variables.
9093
context.report({
9194
node,

lib/utils/indent-ts.js

Lines changed: 2 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const {
1212
isClosingBracketToken,
1313
isOpeningBracketToken
1414
} = require('eslint-utils')
15+
const { isTypeNode } = require('./ts-ast-utils')
1516

1617
/**
1718
* @typedef {import('../../typings/eslint-plugin-vue/util-types/indent-helper').TSNodeListener} TSNodeListener
@@ -224,46 +225,9 @@ function defineVisitor({
224225
*/
225226
// eslint-disable-next-line complexity -- ignore
226227
'*[type=/^TS/]'(node) {
227-
if (
228-
node.type !== 'TSAnyKeyword' &&
229-
node.type !== 'TSArrayType' &&
230-
node.type !== 'TSBigIntKeyword' &&
231-
node.type !== 'TSBooleanKeyword' &&
232-
node.type !== 'TSConditionalType' &&
233-
node.type !== 'TSConstructorType' &&
234-
node.type !== 'TSFunctionType' &&
235-
node.type !== 'TSImportType' &&
236-
node.type !== 'TSIndexedAccessType' &&
237-
node.type !== 'TSInferType' &&
238-
node.type !== 'TSIntersectionType' &&
239-
node.type !== 'TSIntrinsicKeyword' &&
240-
node.type !== 'TSLiteralType' &&
241-
node.type !== 'TSMappedType' &&
242-
node.type !== 'TSNamedTupleMember' &&
243-
node.type !== 'TSNeverKeyword' &&
244-
node.type !== 'TSNullKeyword' &&
245-
node.type !== 'TSNumberKeyword' &&
246-
node.type !== 'TSObjectKeyword' &&
247-
node.type !== 'TSOptionalType' &&
248-
node.type !== 'TSRestType' &&
249-
node.type !== 'TSStringKeyword' &&
250-
node.type !== 'TSSymbolKeyword' &&
251-
node.type !== 'TSTemplateLiteralType' &&
252-
node.type !== 'TSThisType' &&
253-
node.type !== 'TSTupleType' &&
254-
node.type !== 'TSTypeLiteral' &&
255-
node.type !== 'TSTypeOperator' &&
256-
node.type !== 'TSTypePredicate' &&
257-
node.type !== 'TSTypeQuery' &&
258-
node.type !== 'TSTypeReference' &&
259-
node.type !== 'TSUndefinedKeyword' &&
260-
node.type !== 'TSUnionType' &&
261-
node.type !== 'TSUnknownKeyword' &&
262-
node.type !== 'TSVoidKeyword'
263-
) {
228+
if (!isTypeNode(node)) {
264229
return
265230
}
266-
/** @type {TypeNode} */
267231
const typeNode = node
268232
if (/** @type {any} */ (typeNode.parent).type === 'TSParenthesizedType') {
269233
return

lib/utils/index.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ const { traverseNodes, getFallbackKeys } = vueEslintParser.AST
5454
const { findVariable } = require('eslint-utils')
5555
const {
5656
getComponentPropsFromTypeDefine,
57-
getComponentEmitsFromTypeDefine
57+
getComponentEmitsFromTypeDefine,
58+
isTypeNode
5859
} = require('./ts-ast-utils')
5960

6061
/**
@@ -1717,6 +1718,10 @@ module.exports = {
17171718
* Retrieve `ChainExpression#expression` value if the given node a `ChainExpression` node. Otherwise, pass through it.
17181719
*/
17191720
skipChainExpression,
1721+
/**
1722+
* Checks whether the given node is in a type annotation.
1723+
*/
1724+
withinTypeNode,
17201725
findVariableByIdentifier,
17211726
getScope,
17221727
/**
@@ -2226,6 +2231,23 @@ function skipChainExpression(node) {
22262231
return node
22272232
}
22282233

2234+
/**
2235+
* Checks whether the given node is in a type annotation.
2236+
* @param {ESNode} node
2237+
* @returns {boolean}
2238+
*/
2239+
function withinTypeNode(node) {
2240+
/** @type {ASTNode | null} */
2241+
let target = node
2242+
while (target) {
2243+
if (isTypeNode(target)) {
2244+
return true
2245+
}
2246+
target = target.parent
2247+
}
2248+
return false
2249+
}
2250+
22292251
/**
22302252
* Gets the property name of a given node.
22312253
* @param {Property|AssignmentProperty|MethodDefinition|MemberExpression} node - The node to get.

lib/utils/ts-ast-utils.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const { findVariable } = require('eslint-utils')
44
* @typedef {import('@typescript-eslint/types').TSESTree.TSInterfaceBody} TSInterfaceBody
55
* @typedef {import('@typescript-eslint/types').TSESTree.TSTypeLiteral} TSTypeLiteral
66
* @typedef {import('@typescript-eslint/types').TSESTree.Parameter} TSESTreeParameter
7+
* @typedef {import('@typescript-eslint/types').TSESTree.Node} Node
78
*
89
*/
910
/**
@@ -12,10 +13,55 @@ const { findVariable } = require('eslint-utils')
1213
*/
1314

1415
module.exports = {
16+
isTypeNode,
1517
getComponentPropsFromTypeDefine,
1618
getComponentEmitsFromTypeDefine
1719
}
1820

21+
/**
22+
* @param {Node | ASTNode} node
23+
* @returns {node is TypeNode}
24+
*/
25+
function isTypeNode(node) {
26+
return (
27+
node.type === 'TSAnyKeyword' ||
28+
node.type === 'TSArrayType' ||
29+
node.type === 'TSBigIntKeyword' ||
30+
node.type === 'TSBooleanKeyword' ||
31+
node.type === 'TSConditionalType' ||
32+
node.type === 'TSConstructorType' ||
33+
node.type === 'TSFunctionType' ||
34+
node.type === 'TSImportType' ||
35+
node.type === 'TSIndexedAccessType' ||
36+
node.type === 'TSInferType' ||
37+
node.type === 'TSIntersectionType' ||
38+
node.type === 'TSIntrinsicKeyword' ||
39+
node.type === 'TSLiteralType' ||
40+
node.type === 'TSMappedType' ||
41+
node.type === 'TSNamedTupleMember' ||
42+
node.type === 'TSNeverKeyword' ||
43+
node.type === 'TSNullKeyword' ||
44+
node.type === 'TSNumberKeyword' ||
45+
node.type === 'TSObjectKeyword' ||
46+
node.type === 'TSOptionalType' ||
47+
node.type === 'TSRestType' ||
48+
node.type === 'TSStringKeyword' ||
49+
node.type === 'TSSymbolKeyword' ||
50+
node.type === 'TSTemplateLiteralType' ||
51+
node.type === 'TSThisType' ||
52+
node.type === 'TSTupleType' ||
53+
node.type === 'TSTypeLiteral' ||
54+
node.type === 'TSTypeOperator' ||
55+
node.type === 'TSTypePredicate' ||
56+
node.type === 'TSTypeQuery' ||
57+
node.type === 'TSTypeReference' ||
58+
node.type === 'TSUndefinedKeyword' ||
59+
node.type === 'TSUnionType' ||
60+
node.type === 'TSUnknownKeyword' ||
61+
node.type === 'TSVoidKeyword'
62+
)
63+
}
64+
1965
/**
2066
* @param {TypeNode} node
2167
* @returns {node is TSTypeLiteral}

tests/lib/rules/valid-define-emits.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,50 @@ tester.run('valid-define-emits', rule, {
7373
})
7474
</script>
7575
`
76+
},
77+
{
78+
// https://github.com/vuejs/eslint-plugin-vue/issues/1656
79+
filename: 'test.vue',
80+
parserOptions: {
81+
parser: require.resolve('@typescript-eslint/parser')
82+
},
83+
code: `
84+
<script setup lang="ts">
85+
import type { PropType } from 'vue';
86+
87+
type X = string;
88+
89+
const props = defineProps({
90+
myProp: Array as PropType<string[]>,
91+
});
92+
93+
const emit = defineEmits({
94+
myProp: (x: X) => true,
95+
});
96+
</script>
97+
`
98+
},
99+
{
100+
filename: 'test.vue',
101+
parserOptions: {
102+
parser: require.resolve('@typescript-eslint/parser')
103+
},
104+
code: `
105+
<script setup lang="ts">
106+
import type { PropType } from 'vue';
107+
108+
const strList = ['a', 'b', 'c']
109+
const str = 'abc'
110+
111+
const props = defineProps({
112+
myProp: Array as PropType<typeof strList>,
113+
});
114+
115+
const emit = defineEmits({
116+
myProp: (x: typeof str) => true,
117+
});
118+
</script>
119+
`
76120
}
77121
],
78122
invalid: [

tests/lib/rules/valid-define-props.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,50 @@ tester.run('valid-define-props', rule, {
7676
})
7777
</script>
7878
`
79+
},
80+
{
81+
// https://github.com/vuejs/eslint-plugin-vue/issues/1656
82+
filename: 'test.vue',
83+
parserOptions: {
84+
parser: require.resolve('@typescript-eslint/parser')
85+
},
86+
code: `
87+
<script setup lang="ts">
88+
import type { PropType } from 'vue';
89+
90+
type X = string;
91+
92+
const props = defineProps({
93+
myProp: Array as PropType<string[]>,
94+
});
95+
96+
const emit = defineEmits({
97+
myProp: (x: X) => true,
98+
});
99+
</script>
100+
`
101+
},
102+
{
103+
filename: 'test.vue',
104+
parserOptions: {
105+
parser: require.resolve('@typescript-eslint/parser')
106+
},
107+
code: `
108+
<script setup lang="ts">
109+
import type { PropType } from 'vue';
110+
111+
const strList = ['a', 'b', 'c']
112+
const str = 'abc'
113+
114+
const props = defineProps({
115+
myProp: Array as PropType<typeof strList>,
116+
});
117+
118+
const emit = defineEmits({
119+
myProp: (x: typeof str) => true,
120+
});
121+
</script>
122+
`
79123
}
80124
],
81125
invalid: [

0 commit comments

Comments
 (0)