Skip to content

Commit 6585121

Browse files
pnevykgajus
authored andcommitted
feat: add require-compound-type-alias (#365)
1 parent 5e2bbe9 commit 6585121

File tree

5 files changed

+148
-0
lines changed

5 files changed

+148
-0
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
### `require-compound-type-alias`
2+
3+
Requires to make a type alias for all [union](https://flow.org/en/docs/types/unions/) and [intersection](https://flow.org/en/docs/types/intersections/) types. If these are used in "raw" forms it might be tempting to just copy&paste them around the code. However, this brings sort of a source code pollution and unnecessary changes on several parts when these compound types need to be changed.
4+
5+
#### Options
6+
7+
The rule has a string option:
8+
9+
* `"never"`
10+
* `"always"`
11+
12+
The default value is `"always"`.
13+
14+
<!-- assertions require-compound-type-alias -->

src/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import noTypesMissingFileAnnotation from './rules/noTypesMissingFileAnnotation';
1616
import noUnusedExpressions from './rules/noUnusedExpressions';
1717
import noWeakTypes from './rules/noWeakTypes';
1818
import objectTypeDelimiter from './rules/objectTypeDelimiter';
19+
import requireCompoundTypeAlias from './rules/requireCompoundTypeAlias';
1920
import requireExactType from './rules/requireExactType';
2021
import requireParameterType from './rules/requireParameterType';
2122
import requireReturnType from './rules/requireReturnType';
@@ -51,6 +52,7 @@ const rules = {
5152
'no-unused-expressions': noUnusedExpressions,
5253
'no-weak-types': noWeakTypes,
5354
'object-type-delimiter': objectTypeDelimiter,
55+
'require-compound-type-alias': requireCompoundTypeAlias,
5456
'require-exact-type': requireExactType,
5557
'require-parameter-type': requireParameterType,
5658
'require-return-type': requireReturnType,
@@ -94,6 +96,7 @@ export default {
9496
'no-mutable-array': 0,
9597
'no-weak-types': 0,
9698
'object-type-delimiter': 0,
99+
'require-compound-type-alias': 0,
97100
'require-exact-type': 0,
98101
'require-parameter-type': 0,
99102
'require-return-type': 0,

src/rules/requireCompoundTypeAlias.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
const schema = [
2+
{
3+
enum: ['always', 'never'],
4+
type: 'string'
5+
}
6+
];
7+
8+
const create = (context) => {
9+
const always = (context.options[0] || 'always') === 'always';
10+
11+
if (always) {
12+
return {
13+
IntersectionTypeAnnotation (node) {
14+
if (node.parent.type !== 'TypeAlias') {
15+
context.report({
16+
message: 'All intersection types must be declared with named type alias.',
17+
node
18+
});
19+
}
20+
},
21+
UnionTypeAnnotation (node) {
22+
if (node.parent.type !== 'TypeAlias') {
23+
context.report({
24+
message: 'All union types must be declared with named type alias.',
25+
node
26+
});
27+
}
28+
}
29+
};
30+
} else {
31+
return {};
32+
}
33+
};
34+
35+
export default {
36+
create,
37+
schema
38+
};
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
export default {
2+
invalid: [
3+
{
4+
code: 'function foo(bar: "A" | "B") {}',
5+
errors: [{message: 'All union types must be declared with named type alias.'}]
6+
},
7+
{
8+
code: 'const foo: "A" | "B" = "A";',
9+
errors: [{message: 'All union types must be declared with named type alias.'}]
10+
},
11+
{
12+
code: 'type Foo = { bar: "A" | "B" };',
13+
errors: [{message: 'All union types must be declared with named type alias.'}]
14+
},
15+
{
16+
code: 'function foo(bar: { n: number } | { s: string }) {}',
17+
errors: [{message: 'All union types must be declared with named type alias.'}]
18+
},
19+
{
20+
code: 'function foo(bar: { n: number } & { s: string }) {}',
21+
errors: [{message: 'All intersection types must be declared with named type alias.'}]
22+
},
23+
{
24+
code: 'const foo: { n: number } & { s: string } = { n: 0, s: "" };',
25+
errors: [{message: 'All intersection types must be declared with named type alias.'}]
26+
},
27+
{
28+
code: 'type Foo = { bar: { n: number } & { s: string } };',
29+
errors: [{message: 'All intersection types must be declared with named type alias.'}]
30+
},
31+
{
32+
code: 'function foo(bar: { n: number } & { s: string }) {}',
33+
errors: [{message: 'All intersection types must be declared with named type alias.'}]
34+
}
35+
],
36+
misconfigured: [
37+
{
38+
errors: [
39+
{
40+
data: 'sometimes',
41+
dataPath: '[0]',
42+
keyword: 'enum',
43+
message: 'should be equal to one of the allowed values',
44+
params: {
45+
allowedValues: [
46+
'always',
47+
'never'
48+
]
49+
},
50+
parentSchema: {
51+
enum: [
52+
'always',
53+
'never'
54+
],
55+
type: 'string'
56+
},
57+
schema: [
58+
'always',
59+
'never'
60+
],
61+
schemaPath: '#/items/0/enum'
62+
}
63+
],
64+
options: ['sometimes']
65+
}
66+
],
67+
valid: [
68+
{
69+
code: 'type Foo = "A" | "B";'
70+
},
71+
{
72+
code: 'type Bar = "A" | "B"; function foo(bar: Bar) {}'
73+
},
74+
{
75+
code: 'type Foo = { disjoint: "A", n: number } | { disjoint: "B", s: string };'
76+
},
77+
{
78+
code: 'type Foo = { n: number } & { s: string };'
79+
},
80+
{
81+
code: 'type Bar = { n: number } & { s: string }; function foo(bar: Bar) {}'
82+
},
83+
{
84+
code: 'function foo(bar: "A" | "B") {}',
85+
options: ['never']
86+
},
87+
{
88+
code: 'function foo(bar: { n: number } & { s: string }) {}',
89+
options: ['never']
90+
}
91+
]
92+
};

tests/rules/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const reportingRules = [
2626
'no-unused-expressions',
2727
'no-weak-types',
2828
'object-type-delimiter',
29+
'require-compound-type-alias',
2930
'require-exact-type',
3031
'require-parameter-type',
3132
'require-return-type',

0 commit comments

Comments
 (0)