Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit badd051

Browse files
armano2michalsnik
authored andcommitted
#564 #575 Fix issues with Typescript (#613)
* Add typescript specific changes * Add "as" helper * Fix vue/require-default-prop * Fix vue/require-prop-types * Allow to use `export default (Vue as VueConstructor<Vue>).extend({` syntax Fix issues: #564 #575 * Fix vue/require-prop-type-constructor * Fix vue/require-valid-default-prop * Add more tests in utils
1 parent 2ddcec9 commit badd051

12 files changed

+279
-34
lines changed

‎lib/rules/require-default-prop.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ module.exports = {
3232
* @return {boolean}
3333
*/
3434
function propIsRequired (prop) {
35-
const propRequiredNode = prop.value.properties
35+
const propRequiredNode = utils.unwrapTypes(prop.value).properties
3636
.find(p =>
3737
p.type === 'Property' &&
3838
p.key.name === 'required' &&
@@ -49,7 +49,7 @@ module.exports = {
4949
* @return {boolean}
5050
*/
5151
function propHasDefault (prop) {
52-
const propDefaultNode = prop.value.properties
52+
const propDefaultNode = utils.unwrapTypes(prop.value).properties
5353
.find(p =>
5454
p.key &&
5555
(p.key.name === 'default' || p.key.value === 'default')
@@ -67,7 +67,7 @@ module.exports = {
6767
return propsNode.value.properties
6868
.filter(prop => prop.type === 'Property')
6969
.filter(prop => {
70-
if (prop.value.type !== 'ObjectExpression') {
70+
if (utils.unwrapTypes(prop.value).type !== 'ObjectExpression') {
7171
return true
7272
}
7373

@@ -98,7 +98,7 @@ module.exports = {
9898
* @return {Boolean}
9999
*/
100100
function isBooleanProp (prop) {
101-
const value = prop.value
101+
const value = utils.unwrapTypes(prop.value)
102102

103103
return isValueNodeOfBooleanType(value) || (
104104
value.type === 'ObjectExpression' &&

‎lib/rules/require-prop-type-constructor.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,17 +81,18 @@ module.exports = {
8181

8282
node.value.properties
8383
.forEach(p => {
84-
if (isForbiddenType(p.value) || p.value.type === 'ArrayExpression') {
85-
checkPropertyNode(p.key, p.value)
86-
} else if (p.value.type === 'ObjectExpression') {
87-
const typeProperty = p.value.properties.find(prop =>
84+
const pValue = utils.unwrapTypes(p.value)
85+
if (isForbiddenType(pValue) || pValue.type === 'ArrayExpression') {
86+
checkPropertyNode(p.key, pValue)
87+
} else if (pValue.type === 'ObjectExpression') {
88+
const typeProperty = pValue.properties.find(prop =>
8889
prop.type === 'Property' &&
8990
prop.key.name === 'type'
9091
)
9192

9293
if (!typeProperty) return
9394

94-
checkPropertyNode(p.key, typeProperty.value)
95+
checkPropertyNode(p.key, utils.unwrapTypes(typeProperty.value))
9596
}
9697
})
9798
})

‎lib/rules/require-prop-types.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,13 @@ module.exports = {
4848
return
4949
}
5050
let hasType = true
51-
if (cp.value.type === 'ObjectExpression') { // foo: {
52-
hasType = objectHasType(cp.value)
53-
} else if (cp.value.type === 'ArrayExpression') { // foo: [
54-
hasType = cp.value.elements.length > 0
55-
} else if (cp.value.type === 'FunctionExpression' || cp.value.type === 'ArrowFunctionExpression') {
51+
const cpValue = utils.unwrapTypes(cp.value)
52+
53+
if (cpValue.type === 'ObjectExpression') { // foo: {
54+
hasType = objectHasType(cpValue)
55+
} else if (cpValue.type === 'ArrayExpression') { // foo: [
56+
hasType = cpValue.elements.length > 0
57+
} else if (cpValue.type === 'FunctionExpression' || cpValue.type === 'ArrowFunctionExpression') {
5658
hasType = false
5759
}
5860
if (!hasType) {

‎lib/rules/require-valid-default-prop.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,11 @@ module.exports = {
9797

9898
const properties = props.value.properties.filter(p =>
9999
isPropertyIdentifier(p) &&
100-
p.value.type === 'ObjectExpression'
100+
utils.unwrapTypes(p.value).type === 'ObjectExpression'
101101
)
102102

103103
for (const prop of properties) {
104-
const type = getPropertyNode(prop.value, 'type')
104+
const type = getPropertyNode(utils.unwrapTypes(prop.value), 'type')
105105
if (!type) continue
106106

107107
const typeNames = new Set(getTypes(type.value)
@@ -111,7 +111,7 @@ module.exports = {
111111
// There is no native types detected
112112
if (typeNames.size === 0) continue
113113

114-
const def = getPropertyNode(prop.value, 'default')
114+
const def = getPropertyNode(utils.unwrapTypes(prop.value), 'default')
115115
if (!def) continue
116116

117117
const defType = getValueType(def.value)

‎lib/utils/index.js

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -426,24 +426,32 @@ module.exports = {
426426
* @returns {boolean}
427427
*/
428428
isVueComponent (node) {
429-
const callee = node.callee
429+
if (node.type === 'CallExpression') {
430+
const callee = node.callee
430431

431-
const isFullVueComponent = node.type === 'CallExpression' &&
432-
callee.type === 'MemberExpression' &&
433-
callee.object.type === 'Identifier' &&
434-
callee.object.name === 'Vue' &&
435-
callee.property.type === 'Identifier' &&
436-
['component', 'mixin', 'extend'].indexOf(callee.property.name) > -1 &&
437-
node.arguments.length >= 1 &&
438-
node.arguments.slice(-1)[0].type === 'ObjectExpression'
432+
if (callee.type === 'MemberExpression') {
433+
const calleeObject = this.unwrapTypes(callee.object)
439434

440-
const isDestructedVueComponent = node.type === 'CallExpression' &&
441-
callee.type === 'Identifier' &&
442-
callee.name === 'component' &&
443-
node.arguments.length >= 1 &&
444-
node.arguments.slice(-1)[0].type === 'ObjectExpression'
435+
const isFullVueComponent = calleeObject.type === 'Identifier' &&
436+
calleeObject.name === 'Vue' &&
437+
callee.property.type === 'Identifier' &&
438+
['component', 'mixin', 'extend'].indexOf(callee.property.name) > -1 &&
439+
node.arguments.length >= 1 &&
440+
node.arguments.slice(-1)[0].type === 'ObjectExpression'
441+
442+
return isFullVueComponent
443+
}
445444

446-
return isFullVueComponent || isDestructedVueComponent
445+
if (callee.type === 'Identifier') {
446+
const isDestructedVueComponent = callee.name === 'component' &&
447+
node.arguments.length >= 1 &&
448+
node.arguments.slice(-1)[0].type === 'ObjectExpression'
449+
450+
return isDestructedVueComponent
451+
}
452+
}
453+
454+
return false
447455
},
448456

449457
/**
@@ -671,7 +679,7 @@ module.exports = {
671679
/**
672680
* Parse CallExpression or MemberExpression to get simplified version without arguments
673681
*
674-
* @param {Object} node The node to parse (MemberExpression | CallExpression)
682+
* @param {ASTNode} node The node to parse (MemberExpression | CallExpression)
675683
* @return {String} eg. 'this.asd.qwe().map().filter().test.reduce()'
676684
*/
677685
parseMemberOrCallExpression (node) {
@@ -703,5 +711,14 @@ module.exports = {
703711
}
704712

705713
return parsedCallee.reverse().join('.').replace(/\.\[/g, '[')
714+
},
715+
716+
/**
717+
* Unwrap typescript types like "X as F"
718+
* @param {ASTNode} node
719+
* @return {ASTNode}
720+
*/
721+
unwrapTypes (node) {
722+
return node.type === 'TSAsExpression' ? node.expression : node
706723
}
707724
}

‎package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@
5555
"eslint-plugin-vue-libs": "^3.0.0",
5656
"lodash": "^4.17.4",
5757
"mocha": "^5.2.0",
58-
"nyc": "^12.0.2"
58+
"nyc": "^12.0.2",
59+
"typescript": "^3.1.3",
60+
"typescript-eslint-parser": "^20.0.0"
5961
}
6062
}

‎tests/lib/rules/no-side-effects-in-computed-properties.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,24 @@ ruleTester.run('no-side-effects-in-computed-properties', rule, {
248248
line: 23,
249249
message: 'Unexpected side effect in "test4" computed property.'
250250
}]
251+
},
252+
{
253+
filename: 'test.vue',
254+
code: `
255+
export default Vue.extend({
256+
computed: {
257+
test1() : string {
258+
return this.something.reverse()
259+
}
260+
}
261+
});
262+
`,
263+
parserOptions,
264+
errors: [{
265+
line: 5,
266+
message: 'Unexpected side effect in "test1" computed property.'
267+
}],
268+
parser: 'typescript-eslint-parser'
251269
}
252270
]
253271
})

‎tests/lib/rules/require-default-prop.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,34 @@ ruleTester.run('require-default-prop', rule, {
113113
}
114114
}
115115
`
116+
},
117+
{
118+
filename: 'test.vue',
119+
code: `
120+
export default (Vue as VueConstructor<Vue>).extend({
121+
props: {
122+
a: {
123+
type: String,
124+
required: true
125+
} as PropOptions<string>
126+
}
127+
});
128+
`,
129+
parser: 'typescript-eslint-parser'
130+
},
131+
{
132+
filename: 'test.vue',
133+
code: `
134+
export default Vue.extend({
135+
props: {
136+
a: {
137+
type: String,
138+
required: true
139+
} as PropOptions<string>
140+
}
141+
});
142+
`,
143+
parser: 'typescript-eslint-parser'
116144
}
117145
],
118146

@@ -157,6 +185,40 @@ ruleTester.run('require-default-prop', rule, {
157185
message: `Prop 'f' requires default value to be set.`,
158186
line: 14
159187
}]
188+
},
189+
{
190+
filename: 'test.vue',
191+
code: `
192+
export default (Vue as VueConstructor<Vue>).extend({
193+
props: {
194+
a: {
195+
type: String
196+
} as PropOptions<string>
197+
}
198+
});
199+
`,
200+
parser: 'typescript-eslint-parser',
201+
errors: [{
202+
message: `Prop 'a' requires default value to be set.`,
203+
line: 4
204+
}]
205+
},
206+
{
207+
filename: 'test.vue',
208+
code: `
209+
export default Vue.extend({
210+
props: {
211+
a: {
212+
type: String
213+
} as PropOptions<string>
214+
}
215+
});
216+
`,
217+
parser: 'typescript-eslint-parser',
218+
errors: [{
219+
message: `Prop 'a' requires default value to be set.`,
220+
line: 4
221+
}]
160222
}
161223
]
162224
})

‎tests/lib/rules/require-prop-type-constructor.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,34 @@ ruleTester.run('require-prop-type-constructor', rule, {
136136
message: 'The "d" property should be a constructor.',
137137
line: 7
138138
}]
139+
},
140+
{
141+
filename: 'SomeComponent.vue',
142+
code: `
143+
export default {
144+
props: {
145+
a: {
146+
type: 'String',
147+
default: 10
148+
} as PropOptions<string>,
149+
}
150+
}
151+
`,
152+
output: `
153+
export default {
154+
props: {
155+
a: {
156+
type: String,
157+
default: 10
158+
} as PropOptions<string>,
159+
}
160+
}
161+
`,
162+
errors: [{
163+
message: 'The "a" property should be a constructor.',
164+
line: 5
165+
}],
166+
parser: 'typescript-eslint-parser'
139167
}
140168
]
141169
})

‎tests/lib/rules/require-prop-types.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,34 @@ ruleTester.run('require-prop-types', rule, {
113113
}
114114
`,
115115
parserOptions: { ecmaVersion: 6, sourceType: 'module' }
116+
},
117+
{
118+
filename: 'test.vue',
119+
code: `
120+
export default (Vue as VueConstructor<Vue>).extend({
121+
props: {
122+
foo: {
123+
type: String
124+
} as PropOptions<string>
125+
}
126+
});
127+
`,
128+
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
129+
parser: 'typescript-eslint-parser'
130+
},
131+
{
132+
filename: 'test.vue',
133+
code: `
134+
export default Vue.extend({
135+
props: {
136+
foo: {
137+
type: String
138+
} as PropOptions<string>
139+
}
140+
});
141+
`,
142+
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
143+
parser: 'typescript-eslint-parser'
116144
}
117145
],
118146

@@ -190,6 +218,38 @@ ruleTester.run('require-prop-types', rule, {
190218
message: 'Prop "foo" should define at least its type.',
191219
line: 4
192220
}]
221+
},
222+
{
223+
filename: 'test.vue',
224+
code: `
225+
export default Vue.extend({
226+
props: {
227+
foo: {} as PropOptions<string>
228+
}
229+
});
230+
`,
231+
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
232+
parser: 'typescript-eslint-parser',
233+
errors: [{
234+
message: 'Prop "foo" should define at least its type.',
235+
line: 4
236+
}]
237+
},
238+
{
239+
filename: 'test.vue',
240+
code: `
241+
export default (Vue as VueConstructor<Vue>).extend({
242+
props: {
243+
foo: {} as PropOptions<string>
244+
}
245+
});
246+
`,
247+
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
248+
parser: 'typescript-eslint-parser',
249+
errors: [{
250+
message: 'Prop "foo" should define at least its type.',
251+
line: 4
252+
}]
193253
}
194254
]
195255
})

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /