Skip to content
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

feat: add require-exact-type rule (fixes #304) #305

Merged
merged 2 commits into from
Jan 25, 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
1 change: 1 addition & 0 deletions .README/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ When `true`, only checks files with a [`@flow` annotation](http://flowtype.org/d
{"gitdown": "include", "file": "./rules/no-unused-expressions.md"}
{"gitdown": "include", "file": "./rules/no-weak-types.md"}
{"gitdown": "include", "file": "./rules/object-type-delimiter.md"}
{"gitdown": "include", "file": "./rules/require-exact-type.md"}
{"gitdown": "include", "file": "./rules/require-parameter-type.md"}
{"gitdown": "include", "file": "./rules/require-return-type.md"}
{"gitdown": "include", "file": "./rules/require-valid-file-annotation.md"}
Expand Down
32 changes: 32 additions & 0 deletions .README/rules/require-exact-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
### `require-exact-type`

This rule enforces [exact object types](https://flow.org/en/docs/types/objects/#toc-exact-object-types).

#### Options

The rule has one string option:

* `"always"` (default): Report all object type definitions that aren't exact.
* `"never"`: Report all object type definitions that are exact.

```js
{
"rules": {
"flowtype/require-exact-type": [
2,
"always"
]
}
}

{
"rules": {
"flowtype/require-exact-type": [
2,
"never"
]
}
}
```

<!-- assertions requireExactType -->
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Run with `npm run lint`.

1. Create a file in `tests/rules/assertions` named the `camelCase` version of your rule name with the following template:
* `export default { invalid: [], valid: [] }`
2. Add your test file to `tests/index.js`
2. Add your test file to `tests/rules/index.js`
3. Create a file in `src/rules` named the `camelCase` version of your rule name
4. Add your rule file to `src/index.js`

Expand Down
159 changes: 159 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@
* [`delimiter-dangle`](#eslint-plugin-flowtype-rules-delimiter-dangle)
* [`generic-spacing`](#eslint-plugin-flowtype-rules-generic-spacing)
* [`no-dupe-keys`](#eslint-plugin-flowtype-rules-no-dupe-keys)
* [`no-flow-fix-me-comments`](#eslint-plugin-flowtype-rules-no-flow-fix-me-comments)
* [`no-mutable-array`](#eslint-plugin-flowtype-rules-no-mutable-array)
* [`no-primitive-constructor-types`](#eslint-plugin-flowtype-rules-no-primitive-constructor-types)
* [`no-types-missing-file-annotation`](#eslint-plugin-flowtype-rules-no-types-missing-file-annotation)
* [`no-unused-expressions`](#eslint-plugin-flowtype-rules-no-unused-expressions)
* [`no-weak-types`](#eslint-plugin-flowtype-rules-no-weak-types)
* [`object-type-delimiter`](#eslint-plugin-flowtype-rules-object-type-delimiter)
* [`require-exact-type`](#eslint-plugin-flowtype-rules-require-exact-type)
* [`require-parameter-type`](#eslint-plugin-flowtype-rules-require-parameter-type)
* [`require-return-type`](#eslint-plugin-flowtype-rules-require-return-type)
* [`require-valid-file-annotation`](#eslint-plugin-flowtype-rules-require-valid-file-annotation)
Expand Down Expand Up @@ -252,6 +254,9 @@ var a: AType<BType>
type A = AType
// Additional rules: {"no-undef":2}

declare type A = number
// Additional rules: {"no-undef":2}

opaque type A = AType
// Additional rules: {"no-undef":2}

Expand Down Expand Up @@ -285,6 +290,9 @@ class C implements AType {}
interface AType {}
// Additional rules: {"no-undef":2}

declare interface A {}
// Additional rules: {"no-undef":2}

({ a: ({b() {}}: AType) })
// Additional rules: {"no-undef":2}

Expand All @@ -309,6 +317,9 @@ var a: AType<BType>
type A = AType
// Additional rules: {"no-undef":2,"no-use-before-define":[2,"nofunc"]}

declare type A = number
// Additional rules: {"no-undef":2,"no-use-before-define":[2,"nofunc"]}

opaque type A = AType
// Additional rules: {"no-undef":2,"no-use-before-define":[2,"nofunc"]}

Expand Down Expand Up @@ -342,6 +353,9 @@ class C implements AType {}
interface AType {}
// Additional rules: {"no-undef":2,"no-use-before-define":[2,"nofunc"]}

declare interface A {}
// Additional rules: {"no-undef":2,"no-use-before-define":[2,"nofunc"]}

({ a: ({b() {}}: AType) })
// Additional rules: {"no-undef":2,"no-use-before-define":[2,"nofunc"]}

Expand Down Expand Up @@ -968,6 +982,31 @@ var a = 1; var b = 1; type f = { get(key: a): string, get(key: b): string }



<a name="eslint-plugin-flowtype-rules-no-flow-fix-me-comments"></a>
### <code>no-flow-fix-me-comments</code>

Disallows `$FlowFixMe` comment suppressions.

This is especially useful as a warning to ensure instances of `$FlowFixMe` in your codebase get fixed over time.

<a name="eslint-plugin-flowtype-rules-no-flow-fix-me-comments-options"></a>
#### Options

This rule takes an optional RegExp that comments a text RegExp that makes the supression valid.

```js
{
"rules": {
"flowtype/no-flow-fix-me-comments": [
1,
"TODO\s+[0-9]+"
]
}
}
```

<!-- assertions no-flow-fix-me-comments -->

<a name="eslint-plugin-flowtype-rules-no-mutable-array"></a>
### <code>no-mutable-array</code>

Expand Down Expand Up @@ -1500,6 +1539,95 @@ type Foo = { a: Foo, b: Bar }



<a name="eslint-plugin-flowtype-rules-require-exact-type"></a>
### <code>require-exact-type</code>

This rule enforces [exact object types](https://flow.org/en/docs/types/objects/#toc-exact-object-types).

<a name="eslint-plugin-flowtype-rules-require-exact-type-options"></a>
#### Options

The rule has one string option:

* `"always"` (default): Report all object type definitions that aren't exact.
* `"never"`: Report all object type definitions that are exact.

```js
{
"rules": {
"flowtype/require-exact-type": [
2,
"always"
]
}
}

{
"rules": {
"flowtype/require-exact-type": [
2,
"never"
]
}
}
```

The following patterns are considered problems:

```js
type foo = {};
// Message: Type identifier 'foo' must be exact.

type foo = { bar: string };
// Message: Type identifier 'foo' must be exact.

// Options: ["always"]
type foo = {};
// Message: Type identifier 'foo' must be exact.

// Options: ["always"]
type foo = { bar: string };
// Message: Type identifier 'foo' must be exact.

// Options: ["never"]
type foo = {| |};
// Message: Type identifier 'foo' must not be exact.

// Options: ["never"]
type foo = {| bar: string |};
// Message: Type identifier 'foo' must not be exact.
```

The following patterns are not considered problems:

```js
type foo = {| |};

type foo = {| bar: string |};

type foo = number;

// Options: ["always"]
type foo = {| |};

// Options: ["always"]
type foo = {| bar: string |};

// Options: ["always"]
type foo = number;

// Options: ["never"]
type foo = { };

// Options: ["never"]
type foo = { bar: string };

// Options: ["never"]
type foo = number;
```



<a name="eslint-plugin-flowtype-rules-require-parameter-type"></a>
### <code>require-parameter-type</code>

Expand Down Expand Up @@ -1789,6 +1917,19 @@ async () => {}
async function x() {}
// Message: Missing return type annotation.

// Options: ["always",{"annotateUndefined":"always"}]
class Test { constructor() { } }
// Message: Must annotate undefined return type.

class Test { foo() { return 42; } }
// Message: Missing return type annotation.

class Test { foo = () => { return 42; } }
// Message: Missing return type annotation.

class Test { foo = () => 42; }
// Message: Missing return type annotation.

// Options: ["always"]
async () => { return; }
// Message: Missing return type annotation.
Expand Down Expand Up @@ -1869,6 +2010,24 @@ async function doThing(): Promise<void> {}
// Options: ["always",{"annotateUndefined":"always"}]
function* doThing(): Generator<number, void, void> { yield 2; }

// Options: ["always",{"annotateUndefined":"always","excludeMatching":["constructor"]}]
class Test { constructor() { } }

class Test { constructor() { } }

// Options: ["always",{"excludeMatching":["foo"]}]
class Test { foo() { return 42; } }

// Options: ["always",{"excludeMatching":["foo"]}]
class Test { foo = () => { return 42; } }

// Options: ["always",{"excludeMatching":["foo"]}]
class Test { foo = () => 42; }

class Test { foo = (): number => { return 42; } }

class Test { foo = (): number => 42; }

async (foo): Promise<number> => { return 3; }

// Options: ["always",{"excludeArrowFunctions":true}]
Expand Down
3 changes: 3 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import noTypesMissingFileAnnotation from './rules/noTypesMissingFileAnnotation';
import noUnusedExpressions from './rules/noUnusedExpressions';
import noWeakTypes from './rules/noWeakTypes';
import objectTypeDelimiter from './rules/objectTypeDelimiter';
import requireExactType from './rules/requireExactType';
import requireParameterType from './rules/requireParameterType';
import requireReturnType from './rules/requireReturnType';
import requireValidFileAnnotation from './rules/requireValidFileAnnotation';
Expand Down Expand Up @@ -40,6 +41,7 @@ const rules = {
'no-unused-expressions': noUnusedExpressions,
'no-weak-types': noWeakTypes,
'object-type-delimiter': objectTypeDelimiter,
'require-exact-type': requireExactType,
'require-parameter-type': requireParameterType,
'require-return-type': requireReturnType,
'require-valid-file-annotation': requireValidFileAnnotation,
Expand Down Expand Up @@ -79,6 +81,7 @@ export default {
'no-mutable-array': 0,
'no-weak-types': 0,
'object-type-delimiter': 0,
'require-exact-type': 0,
'require-parameter-type': 0,
'require-return-type': 0,
'require-variable-type': 0,
Expand Down
39 changes: 39 additions & 0 deletions src/rules/requireExactType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const schema = [
{
enum: ['always', 'never'],
type: 'string'
}
];

const create = (context) => {
const always = (context.options[0] || 'always') === 'always';

return {
TypeAlias (node) {
const {id: {name}, right: {type, exact}} = node;

if (type === 'ObjectTypeAnnotation') {
if (always && !exact) {
context.report({
data: {name},
message: 'Type identifier \'{{name}}\' must be exact.',
node
});
}

if (!always && exact) {
context.report({
data: {name},
message: 'Type identifier \'{{name}}\' must not be exact.',
node
});
}
}
}
};
};

export default {
create,
schema
};
Loading