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

TSLint rules to disable mutation in TypeScript.

  • 3.0.0
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
19K
decreased by-4.56%
Maintainers
1
Weekly downloads
 
Created
Source

tslint-immutable

npm version travis build MIT license

TSLint rules to disable mutation in TypeScript.

Background

In some applications it is important to not mutate any data, for example when using Redux to store state in a React application. Moreover immutable data structures has a lot of advantages in general so I want to use them everywhere in my applications.

I originally used immutablejs for this purpose. It is a really nice library but I found it had some drawbacks. Specifically when debugging it was hard to see the structure, creating JSON was not straightforward, and passing parameters to other libraries required converting to regular mutable arrays and objects. The seamless-immutable project seems to have the same conclusions and they use regular objects and arrays and check for immutability at run-time. This solves all the aformentioned drawbacks but introduces a new drawback of only being enforced at run-time. (Altough you loose the structural sharing feature of immutablejs with this solution so you would have to consider if that is something you need).

Then typescript 2.0 came along and introduced readonly options for properties, indexers and arrays. This enables us to use regular object and arrays and have the immutability enfored at compile time instead of run-time. Now the only drawback is that there is nothing enforcing the use of readonly in typescript.

This can be solved by using linting rules. So the aim of this project is to leverage the type system in typescript to enforce immutability at compile-time while still using regular objects and arrays.

Installing

npm install tslint-immutable --save-dev

Compability

  • tslint-immutable 3.x.x is compatible with tslint 5.x.x.
  • tslint-immutable 2.x.x is compatible with tslint 4.x.x.
  • tslint-immutable 1.x.x is compatible with tslint 3.x.x.

TSLint Rules

In addition to immutable rules this project also contains a few rules for enforcing a functional style of programming and a few other rules. The following rules are available:

Immutability rules

readonly-interface

This rule enforces having the readonly modifier on all interface members.

You might think that using const would eliminate mutation from your TypeScript code. Wrong. Turns out that there's a pretty big loophole in const.

interface Point { x: number, y: number }
const point: Point = { x: 23, y: 44 };
point.x = 99; // This is legal

This is why the readonly-interface rule exists. This rule prevents you from assigning a value to the result of a member expression.

interface Point { readonly x: number, readonly y: number }
const point: Point = { x: 23, y: 44 };
point.x = 99; // <- No object mutation allowed.

This rule is just as effective as using Object.freeze() to prevent mutations in your Redux reducers. However this rule has no run-time cost, and is enforced at compile time. A good alternative to object mutation is to use the ES2016 object spread syntax that was added in typescript 2.1:

interface Point { readonly x: number, readonly y: number }
const point: Point = { x: 23, y: 44 };
const transformedPoint = { ...point, x: 99 };
readonly-indexer

This rule enforces all indexers to have the readonly modifier.

// NOT OK
let foo: { [key:string]: number }; 
// OK
let foo: { readonly [key:string]: number }; 
readonly-array

This rule enforces use of ReadonlyArray<T> instead of Array<T> or T[].

Even if an array is declared with const it is still possible to mutate the contents of the array.

interface Point { readonly x: number, readonly y: number }
const points: Array<Point> = [{ x: 23, y: 44 }];
points.push({ x: 1, y: 2 }); // This is legal

Using the readonly-array rule will stop this mutation:

interface Point { readonly x: number, readonly y: number }
const points: ReadonlyArray<Point> = [{ x: 23, y: 44 }];
points.push({ x: 1, y: 2 }); // Unresolved method push()
no-let

This rule should be combined with tslint's built-in no-var 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.

let x = 5; // <- Unexpected let or var, use const.

What about for loops? Loops can be replaced with the Array methods like map, filter, and so on. If you find the built-in JS Array methods lacking, use ramda, or lodash-fp.

const SearchResults = 
  ({ results }) => 
    <ul>{
      results.map(result => <li>result</li>) // <- Who needs let?
    }</ul>;
no-this, no-class, no-new

Thanks to libraries like recompose and Redux's React Container components, there's not much reason to build Components using React.createClass or ES6 classes anymore. The no-this rule makes this explicit.

const Message = React.createClass({
  render: function() {
    return <div>{ this.props.message }</div>; // <- no this allowed
  }
})

Instead of creating classes, you should use React 0.14's Stateless Functional Components and save yourself some keystrokes:

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

What about lifecycle methods like shouldComponentUpdate? We can use the recompose library to apply these optimizations to your Stateless Functional Components. The recompose library relies on the fact that your Redux state is immutable to efficiently implement shouldComponentUpdate for you.

import { pure, onlyUpdateForKeys } from 'recompose';

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

// Optimized version of same component, using shallow comparison of props
// Same effect as React's PureRenderMixin
const OptimizedMessage = pure(Message);

// Even more optimized: only updates if specific prop keys have changed
const HyperOptimizedMessage = onlyUpdateForKeys(['message'], Message);

Functional style rules

no-mixed-interface

Mixing functions and data properties in the same interface is a sign of object-orientation style. This rule enforces that an inteface only has one type of members, eg. only data properties or only functions.

no-expression-statement

When you call a function and don’t use it’s return value, chances are high that it is being called for its side effect. e.g.

array.push(1)
alert('Hello world!')

This rule checks that the value of an expression is assigned to a variable and thus helps promote side-effect free (pure) functions.

Other rules

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

Disallows use of the arguments keyword.

no-label

Disallows the use of labels, and indirectly also goto.

no-semicolon-interface

Ensures that interfaces only use commas as separator instead semicolor.

// This is NOT ok.
inferface Foo {
  bar: string;
  zoo(): number;
}
// This is ok.
inferface Foo {
  bar: string,
  zoo(): number,
}

Sample Configuration File

Here's a sample TSLint configuration file (tslint.json) that activates all the recommended rules:

{
  "rulesDirectory": "path/to/tslint-immutable/rules",
  "rules": {
    "no-let": true,
    "no-this": true,
    "no-expression-statement": true,
    "no-var-keyword": true
  }
}

Prior work

This work was originally inspired by eslint-plugin-immutable.

Keywords

FAQs

Package last updated on 01 Apr 2017

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

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