Skip to content

Commit 8acfba0

Browse files
prepare force types on object implementation
1 parent 49d0f30 commit 8acfba0

File tree

3 files changed

+129
-52
lines changed

3 files changed

+129
-52
lines changed

docs/rules/force-types-on-object-props.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ Nothing.
8181

8282
## When Not To Use It
8383

84-
When you're not using TypeScript in the project.
84+
When you're not using TypeScript in the project****.
8585

8686
## Further Reading
8787

Lines changed: 71 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,92 @@
11
/**
2-
* @author *****your name*****
2+
* @author Przemysław Jan Beigert
33
* See LICENSE file in root directory for full license.
44
*/
55
'use strict'
66

7-
// ------------------------------------------------------------------------------
8-
// Requirements
9-
// ------------------------------------------------------------------------------
10-
11-
const utils = require('../utils')
12-
137
// ------------------------------------------------------------------------------
148
// Helpers
159
// ------------------------------------------------------------------------------
1610

17-
// ...
11+
/**
12+
* Check if all keys and values from second object are resent in first object
13+
*
14+
* @param {{ [key: string]: any }} a object to
15+
* @param {{ [key: string]: any }} b The string to escape.
16+
* @returns {boolean} Returns the escaped string.
17+
*/
18+
const isLooksLike = (a, b) => {
19+
return (
20+
a &&
21+
b &&
22+
Object.keys(b).every(bKey => {
23+
const bVal = b[bKey];
24+
const aVal = a[bKey];
25+
if (typeof bVal === 'function') {
26+
return bVal(aVal);
27+
}
28+
return bVal == null || /^[sbn]/.test(typeof bVal) ? bVal === aVal : isLooksLike(aVal, bVal);
29+
})
30+
)
31+
}
1832

19-
// ------------------------------------------------------------------------------
33+
//------------------------------------------------------------------------------
2034
// Rule Definition
21-
// ------------------------------------------------------------------------------
35+
//------------------------------------------------------------------------------
2236

2337
module.exports = {
2438
meta: {
25-
type: 'problem',
2639
docs: {
27-
description: '',
28-
categories: undefined,
29-
url: ''
40+
description: 'Force user to add type declaration to object props',
41+
category: 'type safe',
42+
recommended: false,
3043
},
3144
fixable: null,
3245
schema: [],
33-
messages: {
34-
// ...
35-
}
3646
},
37-
/** @param {RuleContext} context */
47+
/** @param {RuleContext} context */
3848
create(context) {
39-
// ...
49+
return {
50+
/** @param {ExportDefaultDeclaration} node */
51+
ExportDefaultDeclaration(node) {
52+
if (node.declaration.type !== 'ObjectExpression') {
53+
return;
54+
}
55+
if (!Array.isArray(node.declaration.properties)) {
56+
return;
57+
}
4058

41-
return utils.defineTemplateBodyVisitor(context, {
42-
// ...
43-
})
44-
}
45-
}
59+
const property = node.declaration.properties.find(property =>
60+
property.type === 'Property' && isLooksLike(property.key, { type: 'Identifier', name: 'props' }) && property.value.type === 'ObjectExpression')
61+
62+
if (!property || property.type === 'SpreadElement' || !('properties' in property.value)) {
63+
return;
64+
}
65+
66+
property.value.properties
67+
.filter(prop => prop.type === 'Property' && prop.value.type === 'ObjectExpression')
68+
.map(prop => prop.value.properties.find(propValueProperty => {
69+
return isLooksLike(propValueProperty.key, { type: 'Identifier', name: 'type' })
70+
}))
71+
.forEach(prop => {
72+
if (!prop) {
73+
return;
74+
}
75+
if (isLooksLike(prop.value, { type: 'Identifier', name: 'Object' })) {
76+
context.report({ node: prop, message: 'Object props has to be typed' })
77+
}
78+
if (prop.value.type === 'TSAsExpression') {
79+
const { typeAnnotation } = prop.value;
80+
if (
81+
['TSAnyKeyword', 'TSTypeLiteral', 'TSUnknownKeyword', 'TSObjectKeyword'].includes(typeAnnotation.type)
82+
|| !typeAnnotation.typeName
83+
|| typeAnnotation.typeName.name !== 'Prop'
84+
) {
85+
context.report({ node: prop, message: 'Object props should be typed like this: "type: Object as Prop<T>"'})
86+
}
87+
}
88+
})
89+
},
90+
};
91+
},
92+
};

tests/lib/rules/force-types-on-object-props.js

Lines changed: 57 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,40 +7,70 @@
77
const RuleTester = require('eslint').RuleTester
88
const rule = require('../../../lib/rules/force-types-on-object-props')
99

10-
const tester = new RuleTester({
10+
const template = prop => `
11+
<template></template>
12+
<script>
13+
export default {
14+
}
15+
</script>
16+
`;
17+
18+
const ruleTester = new RuleTester({
1119
parser: require.resolve('vue-eslint-parser'),
12-
parserOptions: {
13-
ecmaVersion: 2020,
14-
sourceType: 'module'
15-
}
20+
parserOptions: { ecmaVersion: 2015 }
1621
})
1722

18-
tester.run('force-types-on-object-props', rule, {
23+
ruleTester.run('force-types-on-object-props', rule, {
1924
valid: [
20-
{
21-
filename: 'test.vue',
22-
code: `
23-
<template>
24-
25-
</template>
26-
`
27-
},
25+
`
26+
<script lang="ts">
27+
export default {
28+
}
29+
</script>
30+
`,
31+
`
32+
<script lang="ts">
33+
export default {
34+
props: {}
35+
}
36+
</script>
37+
`,
38+
template('type: Object as Prop<{}>'),
39+
template('type: String'),
40+
template('type: Number'),
41+
template('type: Boolean'),
42+
template('type: [String, Number, Boolean]'),
2843
],
2944
invalid: [
3045
{
31-
filename: 'test.vue',
32-
code: `
33-
<template>
34-
35-
</template>
36-
`,
37-
errors: [
38-
{
39-
message: '...',
40-
line: 'line',
41-
column: 'col'
42-
},
43-
]
46+
code: template('type: Object'),
47+
errors: [{
48+
message: 'Object props has to be typed',
49+
}]
50+
},
51+
{
52+
code: template('type: Object as any'),
53+
errors: [{
54+
message: 'Object props should be typed like this: "type: Object as Prop<T>"',
55+
}]
56+
},
57+
{
58+
code: template('type: Object as {}'),
59+
errors: [{
60+
message: 'Object props should be typed like this: "type: Object as Prop<T>"',
61+
}]
62+
},
63+
{
64+
code: template('type: Object as unknown'),
65+
errors: [{
66+
message: 'Object props should be typed like this: "type: Object as Prop<T>"',
67+
}]
68+
},
69+
{
70+
code: template('type: Object as string'),
71+
errors: [{
72+
message: 'Object props should be typed like this: "type: Object as Prop<T>"',
73+
}]
4474
}
4575
]
4676
})

0 commit comments

Comments
 (0)