Skip to content

#564 #575 Fix issues with Typescript #613

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions lib/rules/require-default-prop.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ module.exports = {
* @return {boolean}
*/
function propIsRequired (prop) {
const propRequiredNode = prop.value.properties
const propRequiredNode = utils.unwrapTypes(prop.value).properties
.find(p =>
p.type === 'Property' &&
p.key.name === 'required' &&
Expand All @@ -49,7 +49,7 @@ module.exports = {
* @return {boolean}
*/
function propHasDefault (prop) {
const propDefaultNode = prop.value.properties
const propDefaultNode = utils.unwrapTypes(prop.value).properties
.find(p =>
p.key &&
(p.key.name === 'default' || p.key.value === 'default')
Expand All @@ -67,7 +67,7 @@ module.exports = {
return propsNode.value.properties
.filter(prop => prop.type === 'Property')
.filter(prop => {
if (prop.value.type !== 'ObjectExpression') {
if (utils.unwrapTypes(prop.value).type !== 'ObjectExpression') {
return true
}

Expand Down Expand Up @@ -98,7 +98,7 @@ module.exports = {
* @return {Boolean}
*/
function isBooleanProp (prop) {
const value = prop.value
const value = utils.unwrapTypes(prop.value)

return isValueNodeOfBooleanType(value) || (
value.type === 'ObjectExpression' &&
Expand Down
11 changes: 6 additions & 5 deletions lib/rules/require-prop-type-constructor.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,18 @@ module.exports = {

node.value.properties
.forEach(p => {
if (isForbiddenType(p.value) || p.value.type === 'ArrayExpression') {
checkPropertyNode(p.key, p.value)
} else if (p.value.type === 'ObjectExpression') {
const typeProperty = p.value.properties.find(prop =>
const pValue = utils.unwrapTypes(p.value)
if (isForbiddenType(pValue) || pValue.type === 'ArrayExpression') {
checkPropertyNode(p.key, pValue)
} else if (pValue.type === 'ObjectExpression') {
const typeProperty = pValue.properties.find(prop =>
prop.type === 'Property' &&
prop.key.name === 'type'
)

if (!typeProperty) return

checkPropertyNode(p.key, typeProperty.value)
checkPropertyNode(p.key, utils.unwrapTypes(typeProperty.value))
}
})
})
Expand Down
12 changes: 7 additions & 5 deletions lib/rules/require-prop-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,13 @@ module.exports = {
return
}
let hasType = true
if (cp.value.type === 'ObjectExpression') { // foo: {
hasType = objectHasType(cp.value)
} else if (cp.value.type === 'ArrayExpression') { // foo: [
hasType = cp.value.elements.length > 0
} else if (cp.value.type === 'FunctionExpression' || cp.value.type === 'ArrowFunctionExpression') {
const cpValue = utils.unwrapTypes(cp.value)

if (cpValue.type === 'ObjectExpression') { // foo: {
hasType = objectHasType(cpValue)
} else if (cpValue.type === 'ArrayExpression') { // foo: [
hasType = cpValue.elements.length > 0
} else if (cpValue.type === 'FunctionExpression' || cpValue.type === 'ArrowFunctionExpression') {
hasType = false
}
if (!hasType) {
Expand Down
6 changes: 3 additions & 3 deletions lib/rules/require-valid-default-prop.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,11 @@ module.exports = {

const properties = props.value.properties.filter(p =>
isPropertyIdentifier(p) &&
p.value.type === 'ObjectExpression'
utils.unwrapTypes(p.value).type === 'ObjectExpression'
)

for (const prop of properties) {
const type = getPropertyNode(prop.value, 'type')
const type = getPropertyNode(utils.unwrapTypes(prop.value), 'type')
if (!type) continue

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

const def = getPropertyNode(prop.value, 'default')
const def = getPropertyNode(utils.unwrapTypes(prop.value), 'default')
if (!def) continue

const defType = getValueType(def.value)
Expand Down
49 changes: 33 additions & 16 deletions lib/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -426,24 +426,32 @@ module.exports = {
* @returns {boolean}
*/
isVueComponent (node) {
const callee = node.callee
if (node.type === 'CallExpression') {
const callee = node.callee

const isFullVueComponent = node.type === 'CallExpression' &&
callee.type === 'MemberExpression' &&
callee.object.type === 'Identifier' &&
callee.object.name === 'Vue' &&
callee.property.type === 'Identifier' &&
['component', 'mixin', 'extend'].indexOf(callee.property.name) > -1 &&
node.arguments.length >= 1 &&
node.arguments.slice(-1)[0].type === 'ObjectExpression'
if (callee.type === 'MemberExpression') {
const calleeObject = this.unwrapTypes(callee.object)

const isDestructedVueComponent = node.type === 'CallExpression' &&
callee.type === 'Identifier' &&
callee.name === 'component' &&
node.arguments.length >= 1 &&
node.arguments.slice(-1)[0].type === 'ObjectExpression'
const isFullVueComponent = calleeObject.type === 'Identifier' &&
calleeObject.name === 'Vue' &&
callee.property.type === 'Identifier' &&
['component', 'mixin', 'extend'].indexOf(callee.property.name) > -1 &&
node.arguments.length >= 1 &&
node.arguments.slice(-1)[0].type === 'ObjectExpression'

return isFullVueComponent
}

return isFullVueComponent || isDestructedVueComponent
if (callee.type === 'Identifier') {
const isDestructedVueComponent = callee.name === 'component' &&
node.arguments.length >= 1 &&
node.arguments.slice(-1)[0].type === 'ObjectExpression'

return isDestructedVueComponent
}
}

return false
},

/**
Expand Down Expand Up @@ -671,7 +679,7 @@ module.exports = {
/**
* Parse CallExpression or MemberExpression to get simplified version without arguments
*
* @param {Object} node The node to parse (MemberExpression | CallExpression)
* @param {ASTNode} node The node to parse (MemberExpression | CallExpression)
* @return {String} eg. 'this.asd.qwe().map().filter().test.reduce()'
*/
parseMemberOrCallExpression (node) {
Expand Down Expand Up @@ -703,5 +711,14 @@ module.exports = {
}

return parsedCallee.reverse().join('.').replace(/\.\[/g, '[')
},

/**
* Unwrap typescript types like "X as F"
* @param {ASTNode} node
* @return {ASTNode}
*/
unwrapTypes (node) {
return node.type === 'TSAsExpression' ? node.expression : node
}
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
"eslint-plugin-vue-libs": "^3.0.0",
"lodash": "^4.17.4",
"mocha": "^5.2.0",
"nyc": "^12.0.2"
"nyc": "^12.0.2",
"typescript": "^3.1.3",
"typescript-eslint-parser": "^20.0.0"
}
}
18 changes: 18 additions & 0 deletions tests/lib/rules/no-side-effects-in-computed-properties.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,24 @@ ruleTester.run('no-side-effects-in-computed-properties', rule, {
line: 23,
message: 'Unexpected side effect in "test4" computed property.'
}]
},
{
filename: 'test.vue',
code: `
export default Vue.extend({
computed: {
test1() : string {
return this.something.reverse()
}
}
});
`,
parserOptions,
errors: [{
line: 5,
message: 'Unexpected side effect in "test1" computed property.'
}],
parser: 'typescript-eslint-parser'
}
]
})
62 changes: 62 additions & 0 deletions tests/lib/rules/require-default-prop.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,34 @@ ruleTester.run('require-default-prop', rule, {
}
}
`
},
{
filename: 'test.vue',
code: `
export default (Vue as VueConstructor<Vue>).extend({
props: {
a: {
type: String,
required: true
} as PropOptions<string>
}
});
`,
parser: 'typescript-eslint-parser'
},
{
filename: 'test.vue',
code: `
export default Vue.extend({
props: {
a: {
type: String,
required: true
} as PropOptions<string>
}
});
`,
parser: 'typescript-eslint-parser'
}
],

Expand Down Expand Up @@ -157,6 +185,40 @@ ruleTester.run('require-default-prop', rule, {
message: `Prop 'f' requires default value to be set.`,
line: 14
}]
},
{
filename: 'test.vue',
code: `
export default (Vue as VueConstructor<Vue>).extend({
props: {
a: {
type: String
} as PropOptions<string>
}
});
`,
parser: 'typescript-eslint-parser',
errors: [{
message: `Prop 'a' requires default value to be set.`,
line: 4
}]
},
{
filename: 'test.vue',
code: `
export default Vue.extend({
props: {
a: {
type: String
} as PropOptions<string>
}
});
`,
parser: 'typescript-eslint-parser',
errors: [{
message: `Prop 'a' requires default value to be set.`,
line: 4
}]
}
]
})
28 changes: 28 additions & 0 deletions tests/lib/rules/require-prop-type-constructor.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,34 @@ ruleTester.run('require-prop-type-constructor', rule, {
message: 'The "d" property should be a constructor.',
line: 7
}]
},
{
filename: 'SomeComponent.vue',
code: `
export default {
props: {
a: {
type: 'String',
default: 10
} as PropOptions<string>,
}
}
`,
output: `
export default {
props: {
a: {
type: String,
default: 10
} as PropOptions<string>,
}
}
`,
errors: [{
message: 'The "a" property should be a constructor.',
line: 5
}],
parser: 'typescript-eslint-parser'
}
]
})
60 changes: 60 additions & 0 deletions tests/lib/rules/require-prop-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,34 @@ ruleTester.run('require-prop-types', rule, {
}
`,
parserOptions: { ecmaVersion: 6, sourceType: 'module' }
},
{
filename: 'test.vue',
code: `
export default (Vue as VueConstructor<Vue>).extend({
props: {
foo: {
type: String
} as PropOptions<string>
}
});
`,
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
parser: 'typescript-eslint-parser'
},
{
filename: 'test.vue',
code: `
export default Vue.extend({
props: {
foo: {
type: String
} as PropOptions<string>
}
});
`,
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
parser: 'typescript-eslint-parser'
}
],

Expand Down Expand Up @@ -190,6 +218,38 @@ ruleTester.run('require-prop-types', rule, {
message: 'Prop "foo" should define at least its type.',
line: 4
}]
},
{
filename: 'test.vue',
code: `
export default Vue.extend({
props: {
foo: {} as PropOptions<string>
}
});
`,
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
parser: 'typescript-eslint-parser',
errors: [{
message: 'Prop "foo" should define at least its type.',
line: 4
}]
},
{
filename: 'test.vue',
code: `
export default (Vue as VueConstructor<Vue>).extend({
props: {
foo: {} as PropOptions<string>
}
});
`,
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
parser: 'typescript-eslint-parser',
errors: [{
message: 'Prop "foo" should define at least its type.',
line: 4
}]
}
]
})
Loading