Skip to content

Commit

Permalink
Merge pull request #22 from jonaskello/rule/immutable-data
Browse files Browse the repository at this point in the history
new rule: immutable-data
  • Loading branch information
Rebecca Stevens committed Jul 15, 2019
2 parents 897ad97 + 6c58379 commit 2dee1ba
Show file tree
Hide file tree
Showing 17 changed files with 935 additions and 1,027 deletions.
3 changes: 1 addition & 2 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,8 @@
"no-var": "error",

// Our rules.
"ts-immutable/immutable-data": "error",
"ts-immutable/no-let": "error",
"ts-immutable/no-array-mutation": "error",
"ts-immutable/no-object-mutation": "error",
"ts-immutable/no-delete": "error",
"ts-immutable/readonly-array": ["error", { "ignoreReturnType": true }],
"ts-immutable/readonly-keyword": "error",
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,7 @@ In addition to immutable rules this project also contains a few rules for enforc
| [`ts-immutable/readonly-keyword`](./docs/rules/readonly-keyword.md) | Enforce readonly modifiers are used where possible | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :wrench: | :thought_balloon: |
| [`ts-immutable/readonly-array`](./docs/rules/readonly-array.md) | Enforce readonly array over mutable arrays | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :wrench: | :thought_balloon: |
| [`ts-immutable/no-let`](./docs/rules/no-let.md) | Disallow mutable variables | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :wrench: | |
| [`ts-immutable/no-array-mutation`](./docs/rules/no-array-mutation.md) | Disallow mutating arrays | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :thought_balloon: |
| [`ts-immutable/no-object-mutation`](./docs/rules/no-object-mutation.md) | Disallow mutating objects | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :blue_heart: |
| [`ts-immutable/immutable-data`](./docs/rules/immutable-data.md) | Disallow mutating objects and arrays | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :blue_heart: |
| [`ts-immutable/no-method-signature`](./docs/rules/no-method-signature.md) | Enforce property signatures with readonly modifiers over method signatures | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :thought_balloon: |
| [`ts-immutable/no-delete`](./docs/rules/no-delete.md) | Disallow delete expressions | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | |

Expand Down
91 changes: 91 additions & 0 deletions docs/rules/immutable-data.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Disallow mutating objects and arrays (immutable-data)

This rule prohibits syntax that mutates existing objects and arrays via assignment to or deletion of their properties/elements.

## Rule Details

While requiring the `readonly` modifier forces declared types to be immutable, it won't stop assignment into or modification of untyped objects or external types declared under different rules.

```typescript
const x = { a: 1 };
const y = [0, 1, 2];

x.foo = "bar"; // <- Modifying an existing object/array is not allowed.
x.a += 1; // <- Modifying an existing object/array is not allowed.
delete x.a; // <- Modifying an existing object/array is not allowed.
Object.assign(x, { b: 2 }); // <- Modifying properties of existing object not allowed.

y[0] = 4; // <- Modifying an array is not allowed.
y.length = 1; // <- Modifying an array is not allowed.
y.push(3); // <- Modifying an array is not allowed.
```

## Options

The rule accepts an options object with the following properties:

```typescript
type Options = {
readonly ignorePattern?: string | Array<string>;
readonly ignoreAccessorPattern?: string | Array<string>;
readonly assumeTypes:
| boolean
| {
readonly forArrays?: boolean;
readonly forObjects?: boolean;
}
};

const defaults = {
assumeTypes: true
};
```

### `assumeTypes`

The rule take advantage of TypeScript's typing engine to check if mutation is taking place.
If you are not using TypeScript, type checking cannot be performed; hence this option exists.

This option will make the rule assume the type of the nodes it is checking are of type Array/Object.
This may however result in some false positives being picked up.

```typescript
const x = [0, 1, 2];
x.push(3); // This will not be flagged as an issue if this option is disabled.
// This is due to the fact that without a typing engine, we cannot tell that x is an array.
```

Note: This option will have no effect if the TypeScript typing engine is avaliable (i.e. you are using TypeScript and have configured eslint correctly).

### `ignorePattern`

See the [ignorePattern](./options/ignore-pattern.md) docs.

### `ignoreAccessorPattern`

This option takes a match string or an array of match strings (not a RegExp pattern).

The match string allows you to specify dot seperated `.` object paths and has support for "glob" `*` and "globstar" `**` matching.

For example:

```js
{
// Ignore all mutations directly on top-level objects that are prefixed with "mutable_".
"ignorePattern": "mutable_*"
}
```

```js
{
// Ignore all mutations directly on all objects, and any of their deeply nested properties, where that object is prefixed with "mutable_".
"ignorePattern": "**.mutable_*.**"
}
```

#### Wildcards

The following wildcards can be used when specifing a pattern:

`**` - Match any depth (including zero). Can only be used as a full accessor.
`*` - When used as a full accessor, match the next accessor. When used as part of an accessor, match any characters.
48 changes: 0 additions & 48 deletions docs/rules/no-array-mutation.md

This file was deleted.

37 changes: 0 additions & 37 deletions docs/rules/no-object-mutation.md

This file was deleted.

28 changes: 0 additions & 28 deletions docs/rules/options/ignore-accessor-pattern.md

This file was deleted.

36 changes: 36 additions & 0 deletions src/common/types-options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { JSONSchema4 } from "json-schema";

export type AssumeTypesOption = {
readonly assumeTypes:
| boolean
| {
readonly forArrays?: boolean;
readonly forObjects?: boolean;
};
};

export const assumeTypesOptionSchema: JSONSchema4 = {
type: "object",
properties: {
assumeTypes: {
oneOf: [
{
type: "boolean"
},
{
type: "object",
properties: {
forArrays: {
type: "boolean"
},
forObjects: {
type: "boolean"
}
},
additionalProperties: false
}
]
}
},
additionalProperties: false
};
3 changes: 1 addition & 2 deletions src/configs/all.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
const config = {
rules: {
"ts-immutable/no-array-mutation": "error",
"ts-immutable/immutable-data": "error",
"ts-immutable/no-class": "error",
"ts-immutable/no-delete": "error",
"ts-immutable/no-expression-statement": "error",
"ts-immutable/no-if-statement": "error",
"ts-immutable/no-let": "error",
"ts-immutable/no-loop-statement": "error",
"ts-immutable/no-object-mutation": "error",
"ts-immutable/no-reject": "error",
"ts-immutable/no-this": "error",
"ts-immutable/no-throw": "error",
Expand Down
3 changes: 1 addition & 2 deletions src/configs/immutable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@ const config = deepMerge([
{
rules: {
"ts-immutable/no-let": "error",
"ts-immutable/no-object-mutation": "error",
"ts-immutable/immutable-data": "error",
"ts-immutable/no-delete": "error"
},
overrides: [
{
files: ["*.ts", "*.tsx"],
rules: {
"ts-immutable/no-array-mutation": "error",
"ts-immutable/no-method-signature": "warn",
"ts-immutable/readonly-array": "error",
"ts-immutable/readonly-keyword": "error"
Expand Down

0 comments on commit 2dee1ba

Please sign in to comment.