diff --git a/.README/rules/no-duplicate-type-union-intersection-members.md b/.README/rules/no-duplicate-type-union-intersection-members.md index fa78b76..1f333d9 100644 --- a/.README/rules/no-duplicate-type-union-intersection-members.md +++ b/.README/rules/no-duplicate-type-union-intersection-members.md @@ -41,3 +41,5 @@ You can disable checking union types using `checkUnions`. } } ``` + + diff --git a/README.md b/README.md index 5d763c8..5b61cb5 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,10 @@ * [`delimiter-dangle`](#eslint-plugin-flowtype-rules-delimiter-dangle) * [`enforce-line-break`](#eslint-plugin-flowtype-rules-enforce-line-break) * [`generic-spacing`](#eslint-plugin-flowtype-rules-generic-spacing) + * [`interface-id-match`](#eslint-plugin-flowtype-rules-interface-id-match) * [`newline-after-flow-annotation`](#eslint-plugin-flowtype-rules-newline-after-flow-annotation) * [`no-dupe-keys`](#eslint-plugin-flowtype-rules-no-dupe-keys) + * [`no-duplicate-type-union-intersection-members`](#eslint-plugin-flowtype-rules-no-duplicate-type-union-intersection-members) * [`no-existential-type`](#eslint-plugin-flowtype-rules-no-existential-type) * [`no-flow-fix-me-comments`](#eslint-plugin-flowtype-rules-no-flow-fix-me-comments) * [`no-internal-flow-type`](#eslint-plugin-flowtype-rules-no-internal-flow-type) @@ -50,6 +52,7 @@ * [`require-variable-type`](#eslint-plugin-flowtype-rules-require-variable-type) * [`semi`](#eslint-plugin-flowtype-rules-semi) * [`sort-keys`](#eslint-plugin-flowtype-rules-sort-keys) + * [`sort-type-union-intersection-members`](#eslint-plugin-flowtype-rules-sort-type-union-intersection-members) * [`space-after-type-colon`](#eslint-plugin-flowtype-rules-space-after-type-colon) * [`space-before-generic-bracket`](#eslint-plugin-flowtype-rules-space-before-generic-bracket) * [`space-before-type-colon`](#eslint-plugin-flowtype-rules-space-before-type-colon) @@ -109,6 +112,10 @@ npm install eslint babel-eslint eslint-plugin-flowtype --save-dev 2, "never" ], + "flowtype/interface-id-match": [ + 2, + "^([A-Z][a-z0-9]+)+Type$" + ], "flowtype/no-mixed": 2, "flowtype/no-primitive-constructor-types": 2, "flowtype/no-types-missing-file-annotation": 2, @@ -1698,6 +1705,24 @@ type fed = "hed"; // Message: New line required below type declaration // Message: New line required above type declaration + +const a = 5; +export type hello = 34; + +// Message: New line required above type declaration + +const a = 5; +// a comment +export type hello = 34; + +// Message: New line required above type declaration + +const a = 5; +/** + * a jsdoc block + */ +type hello = 34; +// Message: New line required above type declaration ``` The following patterns are not considered problems: @@ -1736,6 +1761,9 @@ type Props = { }; type RoadT = "grass" | "gravel" | "cement"; + +// @flow +type A = string ``` @@ -1816,7 +1844,13 @@ type X = Promise<(foo), bar, (((baz)))> type X = Promise< (foo), bar, - (((baz))), + (((baz))) +> + +type X = Promise< + (foo), + bar, + (((baz))) > // Options: ["always"] @@ -1831,12 +1865,60 @@ type X = Promise< (foo), bar, (((baz))) > + +### interface-id-match + +Enforces a consistent naming pattern for interfaces. + + +#### Options + +This rule requires a text RegExp: + +```js +{ + "rules": { + "flowtype/interface-id-match": [ + 2, + "^([A-Z][a-z0-9]*)+Type$" + ] + } +} +``` + +`'^([A-Z][a-z0-9]*)+Type$$'` is the default pattern. + +The following patterns are considered problems: + +```js +interface foo{}; +// Message: Interface identifier 'foo' does not match pattern '/^([A-Z][a-z0-9]*)+Type$/'. + +// Options: ["^foo$"] +interface FooType{}; +// Message: Interface identifier 'FooType' does not match pattern '/^foo$/'. +``` + +The following patterns are not considered problems: + +```js +interface FooType {}; + +// Options: ["^foo$"] +interface foo {}; + +// Settings: {"flowtype":{"onlyFilesWithFlowAnnotation":true}} +interface foo {}; +``` + + + ### newline-after-flow-annotation This rule requires an empty line after the Flow annotation. - + #### Options The rule has a string option: @@ -1870,7 +1952,7 @@ import Foo from './foo'; // Message: Expected newline after flow annotation // Options: ["always-windows"] -// @flow +// @flow import Foo from './foo'; // Message: Expected newline after flow annotation @@ -1890,8 +1972,8 @@ The following patterns are not considered problems: import Foo from './foo'; // Options: ["always-windows"] -// @flow - +// @flow + import Foo from './foo'; // Options: ["never"] @@ -2003,6 +2085,84 @@ export interface Foo { get foo(): boolean; get bar(): string; } + +### no-duplicate-type-union-intersection-members + +_The `--fix` option on the command line automatically fixes problems reported by this rule._ + +Checks for duplicate members of a type union/intersection. + + +#### Options + +You can disable checking intersection types using `checkIntersections`. + +* `true` (default) - check for duplicate members of intersection members. +* `false` - do not check for duplicate members of intersection members. + +```js +{ + "rules": { + "flowtype/no-duplicate-type-union-intersection-members": [ + 2, + { + "checkIntersections": true + } + ] + } +} +``` + +You can disable checking union types using `checkUnions`. + +* `true` (default) - check for duplicate members of union members. +* `false` - do not check for duplicate members of union members. + +```js +{ + "rules": { + "flowtype/no-duplicate-type-union-intersection-members": [ + 2, + { + "checkUnions": true + } + ] + } +} +``` + +The following patterns are considered problems: + +```js +type A = 1 | 2 | 3 | 1; +// Message: Duplicate union member found "1". + +type B = 'foo' | 'bar' | 'foo'; +// Message: Duplicate union member found "'foo'". + +type C = A | B | A | B; +// Message: Duplicate union member found "A". +// Message: Duplicate union member found "B". + +type C = A & B & A & B; +// Message: Duplicate intersection member found "A". +// Message: Duplicate intersection member found "B". +``` + +The following patterns are not considered problems: + +```js +type A = 1 | 2 | 3; + +type B = 'foo' | 'bar'; + +type C = A | B; + +type C = A & B; +``` + + + ### no-existential-type @@ -2046,7 +2206,7 @@ Disallows `$FlowFixMe` comment suppressions. This is especially useful as a warning to ensure instances of `$FlowFixMe` in your codebase get fixed over time. - + #### Options This rule takes an optional RegExp that comments a text RegExp that makes the supression valid. @@ -2670,7 +2830,7 @@ _The `--fix` option on the command line automatically fixes problems reported by This rule enforces consistent spacing inside braces of object types. - + #### Options The rule has a string option: @@ -2935,7 +3095,7 @@ type Foo = { a: Foo, b: Bar } Enforces single quotes or double quotes around string literals. - + #### Options The rule has string options of: @@ -3002,7 +3162,7 @@ type T = { test: 'hello' | 'test', t: 'hello' } 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. - + #### Options The rule has two options: @@ -3028,7 +3188,7 @@ The rule has two options: } ``` -* `allowNull` – allows compound types where one of the members is a `null`, e.g. `string | null`. +* `allowNull` – allows compound types where one of the members is a `null`, e.g. `string | null`. The following patterns are considered problems: @@ -3096,7 +3256,7 @@ _The `--fix` option on the command line automatically fixes problems reported by This rule enforces [exact object types](https://flow.org/en/docs/types/objects/#toc-exact-object-types). - + #### Options The rule has one string option: @@ -3214,6 +3374,11 @@ interface StackFrame { function?: {| name: string |}; } +// Options: ["always"] +declare class MyEvent extends Event { + key: string + } + // Options: ["never"] type foo = { }; @@ -3249,7 +3414,7 @@ _The `--fix` option on the command line automatically fixes problems reported by This rule validates Flow object indexer name. - + #### Options The rule has a string option: @@ -3294,7 +3459,7 @@ type foo = { [string]: number }; This rule enforces explicit inexact object types. - + #### Options The rule has one string option: @@ -3403,7 +3568,7 @@ type foo = number; Requires that all function parameters have type annotations. - + #### Options You can skip all arrow functions by providing the `excludeArrowFunctions` option with `true`. @@ -3775,7 +3940,7 @@ function Foo(props: {}) { return

} Requires that functions have return type annotation. - + #### Options You can skip all arrow functions by providing the `excludeArrowFunctions` option with `true`. @@ -4137,7 +4302,7 @@ async function * foo(): AsyncIterable { yield 2; } Requires all type declarations to be at the top of the file, after any import declarations. - + #### Options The rule has a string option: @@ -4214,7 +4379,7 @@ This rule validates Flow file annotations. This rule can optionally report missing or missed placed annotations, common typos (e.g. `// @floww`), and enforce a consistent annotation style. - + #### Options The rule has a string option: @@ -4407,7 +4572,7 @@ a; Requires that all variable declarators have type annotations. - + #### Options You can exclude variables that match a certain regex by using `excludeVariableMatch`. @@ -4574,7 +4739,7 @@ _The `--fix` option on the command line automatically fixes problems reported by Enforces natural, case-insensitive sorting of Object annotations. - + #### Options The first option specifies sort order. @@ -4628,7 +4793,7 @@ type FooType = { a: number, c: number, b: string } c: number, b: string, } - + // Message: Expected type annotations to be in ascending order. "b" must be before "c". @@ -4637,7 +4802,7 @@ type FooType = { a: number, c: number, b: string } c: $ReadOnlyMap, b: Map>>, } - + // Message: Expected type annotations to be in ascending order. "b" must be before "c". @@ -4663,7 +4828,7 @@ type FooType = { a: number, c: number, b: string } }, }>>>, } - + // Message: Expected type annotations to be in ascending order. "x" must be before "y". // Message: Expected type annotations to be in ascending order. "k" must be before "l". // Message: Expected type annotations to be in ascending order. "b" must be before "c". @@ -4677,7 +4842,7 @@ type FooType = { a: number, c: number, b: string } c: string, b: number, } - + // Message: Expected type annotations to be in ascending order. "b" must be before "c". @@ -4690,7 +4855,7 @@ type FooType = { a: number, c: number, b: string } e: string, d: number, } - + // Message: Expected type annotations to be in ascending order. "b" must be before "c". // Message: Expected type annotations to be in ascending order. "d" must be before "e". @@ -4701,7 +4866,7 @@ type FooType = { a: number, c: number, b: string } c: string, b: number, } - + // Message: Expected type annotations to be in ascending order. "b" must be before "c". @@ -4718,7 +4883,7 @@ type FooType = { a: number, c: number, b: string } b: number, dWithoutComma: boolean } - + // Message: Expected type annotations to be in ascending order. "b" must be before "c". @@ -4727,7 +4892,7 @@ type FooType = { a: number, c: number, b: string } c: number, b: string, } - + // Message: Expected type annotations to be in ascending order. "b" must be before "c". @@ -4736,7 +4901,7 @@ type FooType = { a: number, c: number, b: string } c: number, b: string, } - + // Message: Expected type annotations to be in ascending order. "b" must be before "c". @@ -4745,7 +4910,7 @@ type FooType = { a: number, c: number, b: string } c: ?number, b: string, } - + // Message: Expected type annotations to be in ascending order. "b" must be before "c". @@ -4754,7 +4919,7 @@ type FooType = { a: number, c: number, b: string } c: number, b: (param: string) => number, } - + // Message: Expected type annotations to be in ascending order. "b" must be before "c". @@ -4763,7 +4928,7 @@ type FooType = { a: number, c: number, b: string } c: number, b: (param: string) => number, } - + // Message: Expected type annotations to be in ascending order. "b" must be before "c". @@ -4772,7 +4937,7 @@ type FooType = { a: number, c: number, b: string } a: number | string | boolean, b: (param: string) => number, } - + // Message: Expected type annotations to be in ascending order. "a" must be before "c". @@ -4785,7 +4950,7 @@ type FooType = { a: number, c: number, b: string } a: number | string | boolean, b: (param: string) => number, } - + // Message: Expected type annotations to be in ascending order. "x" must be before "z". // Message: Expected type annotations to be in ascending order. "a" must be before "c". @@ -4803,7 +4968,7 @@ type FooType = { a: number, c: number, b: string } a: number | string | boolean, b: (param: string) => number, } - + // Message: Expected type annotations to be in ascending order. "k" must be before "l". // Message: Expected type annotations to be in ascending order. "x" must be before "z". // Message: Expected type annotations to be in ascending order. "a" must be before "c". @@ -4814,7 +4979,7 @@ type FooType = { a: number, c: number, b: string } -b: number, a: number, } - + // Message: Expected type annotations to be in ascending order. "b" must be before "c". // Message: Expected type annotations to be in ascending order. "a" must be before "b". @@ -4824,7 +4989,7 @@ type FooType = { a: number, c: number, b: string } -b: number, a: number, |} - + // Message: Expected type annotations to be in ascending order. "b" must be before "c". // Message: Expected type annotations to be in ascending order. "a" must be before "b". @@ -4834,7 +4999,7 @@ type FooType = { a: number, c: number, b: string } c: number, b(param: string): number, } - + // Message: Expected type annotations to be in ascending order. "b" must be before "c". @@ -4843,7 +5008,7 @@ type FooType = { a: number, c: number, b: string } c: number, b(param: string): number, } - + // Message: Expected type annotations to be in ascending order. "b" must be before "c". @@ -4852,7 +5017,7 @@ type FooType = { a: number, c: number, b: string } a: number | string | boolean, b(param: string): number, } - + // Message: Expected type annotations to be in ascending order. "a" must be before "c". @@ -4865,7 +5030,7 @@ type FooType = { a: number, c: number, b: string } a: number | string | boolean, b(param: string): number, } - + // Message: Expected type annotations to be in ascending order. "x" must be before "z". // Message: Expected type annotations to be in ascending order. "a" must be before "c". @@ -4883,10 +5048,43 @@ type FooType = { a: number, c: number, b: string } a: number | string | boolean, b(param: string): number, } - + // Message: Expected type annotations to be in ascending order. "k" must be before "l". // Message: Expected type annotations to be in ascending order. "x" must be before "z". // Message: Expected type annotations to be in ascending order. "a" must be before "c". + + + type FooType = { + /* preserves block comment before a */ + a: number | string | boolean, + /* preserves block comment before c */ + c: number, + /* preserves block comment before b */ + b(param: string): number, + } + +// Message: Expected type annotations to be in ascending order. "b" must be before "c". + + + export type GroupOrdersResponseType = {| + isSuccess: boolean, + code: number, + message?: string, + errorMessage: string, + result: {| + OrderNumber: string, + Orders: GroupOrderSummaryType[], + PlacedOn: string, + Status: string, + ReturnText: string, + IncludesLegacyOrder: boolean + |} + |}; + +// Message: Expected type annotations to be in ascending order. "code" must be before "isSuccess". +// Message: Expected type annotations to be in ascending order. "errorMessage" must be before "message". +// Message: Expected type annotations to be in ascending order. "ReturnText" must be before "Status". +// Message: Expected type annotations to be in ascending order. "IncludesLegacyOrder" must be before "ReturnText". ``` The following patterns are not considered problems: @@ -4919,6 +5117,188 @@ type FooType = { a(): string, b: number, c: boolean } + +### sort-type-union-intersection-members + +_The `--fix` option on the command line automatically fixes problems reported by this rule._ + +Enforces that members of a type union/intersection are sorted alphabetically. + + +#### Options + +You can specify the sort order using `order`. + +* `"asc"` (default) - enforce ascending sort order. +* `"desc"` - enforce descending sort order. + +```js +{ + "rules": { + "flowtype/sort-type-union-intersection-members": [ + 2, + { + "order": "asc" + } + ] + } +} +``` + +You can disable checking intersection types using `checkIntersections`. + +* `true` (default) - enforce sort order of intersection members. +* `false` - do not enforce sort order of intersection members. + +```js +{ + "rules": { + "flowtype/sort-type-union-intersection-members": [ + 2, + { + "checkIntersections": true + } + ] + } +} +``` + +You can disable checking union types using `checkUnions`. + +* `true` (default) - enforce sort order of union members. +* `false` - do not enforce sort order of union members. + +```js +{ + "rules": { + "flowtype/sort-type-union-intersection-members": [ + 2, + { + "checkUnions": true + } + ] + } +} +``` + +You can specify the ordering of groups using `groupOrder`. + +Each member of the type is placed into a group, and then the rule sorts alphabetically within each group. +The ordering of groups is determined by this option. + +* `keyword` - Keyword types (`any`, `string`, etc) +* `named` - Named types (`A`, `A['prop']`, `B[]`, `Array`) +* `literal` - Literal types (`1`, `'b'`, `true`, etc) +* `function` - Function types (`() => void`) +* `object` - Object types (`{ a: string }`, `{ [key: string]: number }`) +* `tuple` - Tuple types (`[A, B, C]`) +* `intersection` - Intersection types (`A & B`) +* `union` - Union types (`A | B`) +* `nullish` - `null` and `undefined` + +```js +{ + "rules": { + "flowtype/sort-type-union-intersection-members": [ + 2, + { + "groupOrder": [ + 'keyword', + 'named', + 'literal', + 'function', + 'object', + 'tuple', + 'intersection', + 'union', + 'nullish', + ] + } + ] + } +} +``` + +The following patterns are considered problems: + +```js +type T1 = B | A; +// Message: Expected union members to be in ascending order. "A" should be before "B". + +type T2 = { b: string } & { a: string }; +// Message: Expected intersection members to be in ascending order. "{ a: string }" should be before "{ b: string }". + +type T3 = [1, 2, 4] & [1, 2, 3]; +// Message: Expected intersection members to be in ascending order. "[1, 2, 3]" should be before "[1, 2, 4]". + + + type T4 = + | [1, 2, 4] + | [1, 2, 3] + | { b: string } + | { a: string } + | (() => void) + | (() => string) + | 'b' + | 'a' + | 'b' + | 'a' + | string[] + | number[] + | B + | A + | string + | any; + +// Message: Expected union members to be in ascending order. "[1, 2, 3]" should be before "[1, 2, 4]". +// Message: Expected union members to be in ascending order. "{ b: string }" should be before "[1, 2, 3]". +// Message: Expected union members to be in ascending order. "{ a: string }" should be before "{ b: string }". +// Message: Expected union members to be in ascending order. "() => void" should be before "{ a: string }". +// Message: Expected union members to be in ascending order. "() => string" should be before "() => void". +// Message: Expected union members to be in ascending order. "'b'" should be before "() => string". +// Message: Expected union members to be in ascending order. "'a'" should be before "'b'". +// Message: Expected union members to be in ascending order. "'b'" should be before "'a'". +// Message: Expected union members to be in ascending order. "'a'" should be before "'b'". +// Message: Expected union members to be in ascending order. "string[]" should be before "'a'". +// Message: Expected union members to be in ascending order. "number[]" should be before "string[]". +// Message: Expected union members to be in ascending order. "B" should be before "number[]". +// Message: Expected union members to be in ascending order. "A" should be before "B". +// Message: Expected union members to be in ascending order. "string" should be before "A". +// Message: Expected union members to be in ascending order. "any" should be before "string". +``` + +The following patterns are not considered problems: + +```js +type T1 = A | B; + +type T2 = { a: string } & { b: string }; + +type T3 = [1, 2, 3] & [1, 2, 4]; + + + type T4 = + | any + | string + | A + | B + | number[] + | string[] + | 'a' + | 'a' + | 'b' + | 'b' + | (() => string) + | (() => void) + | { a: string } + | { b: string } + | [1, 2, 3] + | [1, 2, 4]; + +``` + + + ### space-after-type-colon @@ -4926,7 +5306,7 @@ _The `--fix` option on the command line automatically fixes problems reported by Enforces consistent spacing after the type annotation colon. - + #### Options This rule has a string argument. @@ -5004,7 +5384,7 @@ The following patterns are considered problems: { a: string, b: number }) => {} // Message: There must not be a line break after "foo" parameter type annotation colon. -(foo: +(foo: { a: string, b: number }) => {} // Message: There must not be a line break after "foo" parameter type annotation colon. @@ -5392,7 +5772,7 @@ The following patterns are not considered problems: { a: string, b: number }) => {} // Options: ["always",{"allowLineBreak":true}] -(foo: +(foo: { a: string, b: number }) => {} // Options: ["never"] @@ -6295,7 +6675,7 @@ type foo = {test: number}; type bar = {...$Exact} Enforces a consistent naming pattern for type aliases. - + #### Options This rule requires a text RegExp: @@ -6356,7 +6736,7 @@ import {type T, type U, type V} from '...'; import type {T, U, V} from '...'; ``` - + #### Options The rule has a string option: @@ -6719,3 +7099,4 @@ function x(foo: Type = bar()) {} ``` + diff --git a/src/rules/requireExactType.js b/src/rules/requireExactType.js index f6981c8..765b4f1 100644 --- a/src/rules/requireExactType.js +++ b/src/rules/requireExactType.js @@ -21,7 +21,7 @@ const create = (context) => { inexact, } = node; - if (node.parent.type !== 'InterfaceDeclaration' && always && !exact && !inexact && indexers.length === 0) { + if (!['DeclareClass', 'InterfaceDeclaration'].includes(node.parent.type) && always && !exact && !inexact && indexers.length === 0) { context.report({ fix: (fixer) => { return [ diff --git a/tests/rules/assertions/requireExactType.js b/tests/rules/assertions/requireExactType.js index f68a0f9..3e5e0b4 100644 --- a/tests/rules/assertions/requireExactType.js +++ b/tests/rules/assertions/requireExactType.js @@ -184,6 +184,15 @@ export default { function?: {| name: string |}; }`, }, + { + code: `declare class MyEvent extends Event { + key: string + }`, + options: ['always'], + output: `declare class MyEvent extends Event { + key: string + }`, + }, // Never