Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

tslint-immutable

Package Overview
Dependencies
Maintainers
1
Versions
56
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

tslint-immutable - npm Package Compare versions

Comparing version 3.0.0 to 3.1.0

test/rules/readonly-array/default/interface.ts.lint

12

CHANGELOG.md

@@ -8,7 +8,9 @@ # Change Log

## [Unreleased]
### Added
## [v3.1.0] - 2017-04-05
### Added
- `ignore-local` option added to `readonly-array`.
- `ignore-prefix` option added to `readonly-array`.
### Changed
## [v3.0.0] - 2017-04-02

@@ -19,3 +21,3 @@ ### Changed

### Added
- readonly-array now also checks for implicit arrays.
- `readonly-array` now also checks for implicity declared mutable arrays.

@@ -56,3 +58,5 @@ ## [v2.1.1] - 2017-03-29

[Unreleased]: https://github.com/jonaskello/tslint-immutable/compare/v2.1.2...master
[Unreleased]: https://github.com/jonaskello/tslint-immutable/compare/v3.1.0...master
[v3.0.0]: https://github.com/jonaskello/tslint-immutable/compare/v3.0.0...v3.1.0
[v3.0.0]: https://github.com/jonaskello/tslint-immutable/compare/v2.1.1...v3.0.0
[v2.1.2]: https://github.com/jonaskello/tslint-immutable/compare/v2.1.1...v2.1.2

@@ -59,0 +63,0 @@ [v2.1.1]: https://github.com/jonaskello/tslint-immutable/compare/v2.1.0...v2.1.1

{
"name": "tslint-immutable",
"version": "3.0.0",
"version": "3.1.0",
"description": "TSLint rules to disable mutation in TypeScript.",

@@ -28,2 +28,3 @@ "main": "index.js",

"build": "rm -rf rules && tsc -p tsconfig.json",
"lint": "tslint './src/**/*.ts{,x}'",
"test": "yarn build && yarn test:rules",

@@ -30,0 +31,0 @@ "test:rules": "./scripts/test-rules.sh",

@@ -33,2 +33,19 @@ # tslint-immutable

* [Immutability rules](#immutability-rules)
* [readonly-interface](#readonly-interface)
* [readonly-indexer](#readonly-indexer)
* [readonly-array](#readonly-array)
* [no-let](#no-let)
* [Functional style rules](#functional-style-rules)
* [no-this](#no-this-no-class-no-new)
* [no-class](#no-this-no-class-no-new)
* [no-new](#no-this-no-class-no-new)
* [no-mixed-interface](#no-mixed-interface)
* [no-expression-statement](#no-expression-statement)
* [Other rules](#other-rules)
* [no-arguments](#no-arguments)
* [no-label](#no-label)
* [no-semicolon-interface](#no-semicolon-interface)
* [import-containment](#import-containment)
### Immutability rules

@@ -42,3 +59,3 @@

```TypeScript
```typescript
interface Point { x: number, y: number }

@@ -51,3 +68,3 @@ const point: Point = { x: 23, y: 44 };

```TypeScript
```typescript
interface Point { readonly x: number, readonly y: number }

@@ -60,3 +77,3 @@ const point: Point = { x: 23, y: 44 };

```TypeScript
```typescript
interface Point { readonly x: number, readonly y: number }

@@ -71,3 +88,3 @@ const point: Point = { x: 23, y: 44 };

```TypeScript
```typescript
// NOT OK

@@ -85,3 +102,3 @@ let foo: { [key:string]: number };

```TypeScript
```typescript
interface Point { readonly x: number, readonly y: number }

@@ -94,3 +111,3 @@ const points: Array<Point> = [{ x: 23, y: 44 }];

```TypeScript
```typescript
interface Point { readonly x: number, readonly y: number }

@@ -101,8 +118,23 @@ const points: ReadonlyArray<Point> = [{ x: 23, y: 44 }];

#### no-let
This rule should be combined with tslint's built-in `no-var` rule to enforce that all variables are declared as `const`.
Options:
- [ignore-local](#using-the-ignore-local-option)
- [ignore-prefix](#using-the-ignore-prefix-option)
Example config:
```javascript
"readonly-array": true
```
```javascript
"readonly-array": [true, "ignore-local"]
```
```javascript
"readonly-array": [true, "ignore-local", {"ignore-prefix": "mutable"}]
```
#### no-let
This rule should be combined with tslint's built-in `no-var-keyword` rule to enforce that all variables are declared as `const`.
There's no reason to use `let` in a Redux/React application, because all your state is managed by either Redux or React. Use `const` instead, and avoid state bugs altogether.
```TypeScript
```typescript
let x = 5; // <- Unexpected let or var, use const.

@@ -113,3 +145,3 @@ ```

```TypeScript
```typescript
const SearchResults =

@@ -122,6 +154,8 @@ ({ results }) =>

### Functional style rules
#### no-this, no-class, no-new
Thanks to libraries like [recompose](https://github.com/acdlite/recompose) and Redux's [React Container components](http://redux.js.org/docs/basics/UsageWithReact.html), there's not much reason to build Components using `React.createClass` or ES6 classes anymore. The `no-this` rule makes this explicit.
```TypeScript
```typescript
const Message = React.createClass({

@@ -135,3 +169,3 @@ render: function() {

```TypeScript
```typescript
const Message = ({message}) => <div>{ message }</div>;

@@ -142,3 +176,3 @@ ```

```TypeScript
```typescript
import { pure, onlyUpdateForKeys } from 'recompose';

@@ -156,4 +190,2 @@

### Functional style rules
#### no-mixed-interface

@@ -166,3 +198,3 @@

```TypeScript
```typescript
array.push(1)

@@ -176,6 +208,2 @@ alert('Hello world!')

#### import-containment
ECMAScript modules does not have a concept of a library that can span multiple files and share internal members. If you have a set of files that forms an library, and they need to be able to call each other internally without exposing members to other files outside the library set, this rule can be useful.
#### no-arguments

@@ -193,3 +221,3 @@

```TypeScript
```typescript
// This is NOT ok.

@@ -207,14 +235,71 @@ inferface Foo {

#### import-containment
ECMAScript modules does not have a concept of a library that can span multiple files and share internal members. If you have a set of files that forms an library, and they need to be able to call each other internally without exposing members to other files outside the library set, this rule can be useful.
## Options
### Using the `ignore-local` option
> If a tree falls in the woods, does it make a sound?
> If a pure function mutates some local data in order to produce an immutable return value, is that ok?
The quote above is from the [clojure docs](https://clojure.org/reference/transients). In general, it is more important to enforce immutability for state that is passed in and out of functions than for local state used for internal calculations within a function. For example in Redux, the state going in and out of reducers needs to be immutable while the reducer may be allowed to mutate local state in its calculations in order to achieve higher performance. This is what the `ignore-local` option enables. With this option enabled immutability will be enforced everywhere but in local state.
### Using the `ignore-prefix` option
Some languages are immutable by default but allows you to explicitly declare mutable variables. For example in [reason](https://facebook.github.io/reason/) you can declare mutable record fields like this:
```reason
type person = {
name: string,
mutable age: int
};
```
Typescript is not immutable by default but it can be if you use this package. So in order to create an escape hatch similar to how it is done in reason the `ignore-mutable` option can be used. For example if you configure it to ignore variables with names that has the prefix "mutable" you can emulate the above example in typescript like this:
```typescript
type person = {
readonly name: string,
mutableAge: number // This is OK with ignore-prefix = "mutable"
};
```
Yes, variable names like `mutableAge` are ugly, but then again mutation is an ugly business :-).
## Sample Configuration File
Here's a sample TSLint configuration file (tslint.json) that activates all the recommended rules:
Here's a sample TSLint configuration file (tslint.json) that activates all the rules:
```json
```javascript
{
"rulesDirectory": "path/to/tslint-immutable/rules",
"rulesDirectory": ["./node_modules/tslint-immutable/rules"],
"rules": {
// Immutability rules
"readonly-interface": true,
"readonly-indexer": true,
"readonly-array": true,
"no-let": true,
"no-var-keyword": true, // built-in tslint rule
// Functional style rules
"no-this": true,
"no-class": true,
"no-new": true,
"no-mixed-interface": true,
"no-expression-statement": true,
"no-var-keyword": true
// Other rules
"no-arguments": true,
"no-label": true,
"no-semicolon-interface": true,
"import-containment": [ true,
{
"containmentPath": "path/to/libs",
"allowedExternalFileNames": ["index"],
"disallowedInternalFileNames": ["index"]
}]
}

@@ -221,0 +306,0 @@ }

@@ -21,4 +21,3 @@ "use strict";

Rule.prototype.apply = function (sourceFile) {
var walker = new ReadonlyArrayWalker(sourceFile, this.getOptions());
return this.applyWithWalker(walker);
return this.applyWithFunction(sourceFile, walk, parseOptions(this.ruleArguments));
};

@@ -29,32 +28,59 @@ return Rule;

exports.Rule = Rule;
var ReadonlyArrayWalker = (function (_super) {
__extends(ReadonlyArrayWalker, _super);
function ReadonlyArrayWalker() {
return _super !== null && _super.apply(this, arguments) || this;
var OPTION_IGNORE_LOCAL = "ignore-local";
var OPTION_IGNORE_PREFIX = "ignore-prefix";
function parseOptions(options) {
var ignoreLocal = options.indexOf(OPTION_IGNORE_LOCAL) !== -1;
var ignorePrefix;
for (var _i = 0, options_1 = options; _i < options_1.length; _i++) {
var o = options_1[_i];
if (typeof o === "object" && o[OPTION_IGNORE_PREFIX] != null) {
ignorePrefix = o[OPTION_IGNORE_PREFIX];
break;
}
}
ReadonlyArrayWalker.prototype.visitTypeReference = function (node) {
_super.prototype.visitTypeReference.call(this, node);
if (node.typeName.getText() === "Array") {
this.addFailure(this.createFailure(node.typeName.getStart(), node.typeName.getWidth(), Rule.FAILURE_STRING));
return { ignoreLocal: ignoreLocal, ignorePrefix: ignorePrefix };
}
function walk(ctx) {
return ts.forEachChild(ctx.sourceFile, cb);
function cb(node) {
if (ctx.options.ignoreLocal && (node.kind === ts.SyntaxKind.FunctionDeclaration || node.kind === ts.SyntaxKind.ArrowFunction)) {
// skip checking in functions if ignore-local is set
return;
}
};
ReadonlyArrayWalker.prototype.visitArrayLiteralExpression = function (node) {
_super.prototype.visitArrayLiteralExpression.call(this, node);
// If the array literal is used in a variable declaration, the variable
// must have a type spcecified, otherwise it will implicitly be of mutable Array type
if (node.parent && node.parent.kind === ts.SyntaxKind.VariableDeclaration) {
if (node.kind === ts.SyntaxKind.TypeReference && isInvalidArrayTypeReference(node, ctx)) {
ctx.addFailureAtNode(node, Rule.FAILURE_STRING);
}
if (node.kind === ts.SyntaxKind.ArrayLiteralExpression && isInvalidArrayLiteralExpression(node, ctx)) {
var variableDeclarationNode = node.parent;
if (!variableDeclarationNode.type) {
this.addFailure(this.createFailure(variableDeclarationNode.name.getStart(), variableDeclarationNode.name.getWidth(), Rule.FAILURE_STRING));
ctx.addFailureAt(variableDeclarationNode.name.getStart(ctx.sourceFile), variableDeclarationNode.name.getWidth(ctx.sourceFile), Rule.FAILURE_STRING);
}
return ts.forEachChild(node, cb);
}
}
function isInvalidArrayTypeReference(node, ctx) {
if (node.typeName.getText(ctx.sourceFile) === "Array") {
if (ctx.options.ignorePrefix) {
var variableDeclarationNode = node.parent;
if (variableDeclarationNode.name.getText(ctx.sourceFile).substr(0, ctx.options.ignorePrefix.length) === ctx.options.ignorePrefix) {
return false;
}
}
};
ReadonlyArrayWalker.prototype.visitTypeLiteral = function (node) {
_super.prototype.visitTypeLiteral.call(this, node);
// if (node.kind === ts.SyntaxKind.ArrayType) {
if (node.kind === ts.SyntaxKind.ArrayType) {
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING));
return true;
}
return false;
}
function isInvalidArrayLiteralExpression(node, ctx) {
// If the array literal is used in a variable declaration, the variable
// must have a type spcecified, otherwise it will implicitly be of mutable Array type
if (node.parent && node.parent.kind === ts.SyntaxKind.VariableDeclaration) {
var variableDeclarationNode = node.parent;
if (!variableDeclarationNode.type) {
if (ctx.options.ignorePrefix &&
variableDeclarationNode.name.getText(ctx.sourceFile).substr(0, ctx.options.ignorePrefix.length) === ctx.options.ignorePrefix) {
return false;
}
return true;
}
};
return ReadonlyArrayWalker;
}(Lint.RuleWalker));
}
return false;
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc