:wave: If you previously used the rules in tslint-immutable, this package is the ESLint version of those rules. Please see the migration guide for how to migrate.
Features
No mutations
One 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. Additionally, this project will also aim to support disabling mutability for vanilla JavaScript where possible.
No object-orientation
JavaScript is multi-paradigm, allowing both object-oriented and functional programming styles. In order to promote a functional style, the object oriented features of JavaScript need to be disabled.
No statements
In functional programming everything is an expression that produces a value. JavaScript has a lot of syntax that is just statements that does not produce a value. That syntax has to be disabled to promote a functional style.
No exceptions
Functional programming style does not use run-time exceptions. Instead expressions produces values to indicate errors.
Currying
JavaScript functions support syntax that is not compatible with curried functions. To enable currying this syntax has to be disabled.
Stylistic
Make things look nicer (in our opinion).
Installation
JavaScript
npm install eslint eslint-plugin-functional --save-dev
yarn add -D eslint eslint-plugin-functional
Note: If you installed ESLint globally (using the -g
flag with npm or global
with yarn) then you must also install eslint-plugin-functional
globally.
TypeScript
To use this plugin with TypeScript, a TypeScript parser for ESLint is needed.
We recommend @typescript-eslint/parser.
npm install eslint @typescript-eslint/parser eslint-plugin-functional --save-dev
yarn add -D eslint @typescript-eslint/parser eslint-plugin-functional
tsutils is also recommended as a peer dependency but if you are using @typescript-eslint/eslint-plugin then you don't need explicitly add it.
Usage
Add functional
to the plugins section of your .eslintrc
configuration file. Then configure the rules you want to use under the rules section.
{
"plugins": ["functional"],
"rules": {
"functional/rule-name": "error"
}
}
There are several rulesets provided by this plugin.
See below for what they are and what rules are including in each.
Enable rulesets via the "extends" property of your .eslintrc
configuration file.
{
// ...
"extends": [
"plugin:functional/external-recommended",
"plugin:functional/recommended",
"plugin:functional/stylistic"
]
}
With TypeScript
Add @typescript-eslint/parser
to the "parser" filed in your .eslintrc
configuration file.
To use type information, you will need to specify a path to your tsconfig.json
file in the "project" property of "parserOptions".
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json"
}
}
See @typescript-eslint/parser's README.md for more information on the available parser options.
Example Config
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "tsconfig.json"
},
"env": {
"es6": true
},
"plugins": [
"@typescript-eslint",
"functional"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"plugin:functional/external-recommended",
"plugin:functional/recommended",
"plugin:functional/stylistic"
]
}
Rulesets
The following rulesets are made available by this plugin:
Presets:
- Recommended (plugin:functional/recommended)
- Lite (plugin:functional/lite)
Categorized:
- No Mutations (plugin:functional/no-mutations)
- No Object Orientation (plugin:functional/no-object-orientation)
- No Statements (plugin:functional/no-statements)
- No Exceptions (plugin:functional/no-exceptions)
- Currying (plugin:functional/currying)
- Stylistic (plugin:functional/stylistic)
Other:
- All (plugin:functional/all) - Enables all rules defined in this plugin.
- External Recommended (plugin:functional/external-recommended) - Configures recommended rules not defined by this plugin.
The below section gives details on which rules are enabled by each ruleset.
Supported Rules
Key:
Symbol | Meaning |
---|
:hear_no_evil: | Ruleset: Lite This ruleset is designed to enforce a somewhat functional programming code style. |
:speak_no_evil: | Ruleset: Recommended This ruleset is designed to enforce a functional programming code style. |
:wrench: | Fixable Problems found by this rule are potentially fixable with the --fix option. |
:thought_balloon: | Only Available for TypeScript The rule either requires Type Information or only works with TypeScript syntax. |
:blue_heart: | Works better with TypeScript Type Information will be used if available making the rule work in more cases. |
No Mutations Rules
:see_no_evil: = no-mutations
Ruleset.
Name | Description | :see_no_evil: | :hear_no_evil: | :speak_no_evil: | :wrench: | :blue_heart: |
---|
immutable-data | Disallow mutating objects and arrays | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :blue_heart: |
no-let | Disallow mutable variables | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | |
no-method-signature | Enforce property signatures with readonly modifiers over method signatures | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :thought_balloon: |
prefer-readonly-type | Use readonly types and readonly modifiers where possible | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :wrench: | :thought_balloon: |
No Object-Orientation Rules
:see_no_evil: = no-object-orientation
Ruleset.
Name | Description | :see_no_evil: | :hear_no_evil: | :speak_no_evil: | :wrench: | :blue_heart: |
---|
no-class | Disallow classes | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | |
no-mixed-type | Restrict types so that only members of the same kind are allowed in them | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :thought_balloon: |
no-this-expression | Disallow this access | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | |
prefer-type-literal | Use type literals over interfaces | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :thought_balloon: |
No Statements Rules
:see_no_evil: = no-statements
Ruleset.
Name | Description | :see_no_evil: | :hear_no_evil: | :speak_no_evil: | :wrench: | :blue_heart: |
---|
no-conditional-statement | Disallow conditional statements (if and switch statements) | :heavy_check_mark: | | :heavy_check_mark: | | :thought_balloon: |
no-expression-statement | Disallow expressions to cause side-effects | :heavy_check_mark: | | :heavy_check_mark: | | :thought_balloon: |
no-loop-statement | Disallow imperative loops | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | |
no-return-void | Disallow functions that return nothing | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :thought_balloon: |
No Exceptions Rules
:see_no_evil: = no-exceptions
Ruleset.
Name | Description | :see_no_evil: | :hear_no_evil: | :speak_no_evil: | :wrench: | :blue_heart: |
---|
no-promise-reject | Disallow rejecting Promises | | | | | |
no-throw-statement | Disallow throwing exceptions | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | |
no-try-statement | Disallow try-catch[-finally] and try-finally patterns | :heavy_check_mark: | | :heavy_check_mark: | | |
Currying Rules
:see_no_evil: = currying
Ruleset.
Name | Description | :see_no_evil: | :hear_no_evil: | :speak_no_evil: | :wrench: | :blue_heart: |
---|
functional-parameters | Functions must have functional parameters | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | |
Stylistic Rules
:see_no_evil: = stylistic
Ruleset.
Name | Description | :see_no_evil: | :hear_no_evil: | :speak_no_evil: | :wrench: | :blue_heart: |
---|
prefer-tacit | Tacit/Point-Free style. | :heavy_check_mark: | | | :wrench: | :blue_heart: |
Recommended standard rules
In addition to the immutability rules above, there are a few standard rules that need to be enabled to achieve immutability.
These rules are all included in the external-recommended rulesets.
Without this rule, it is still possible to create var
variables that are mutable.
Without this rule, function parameters are mutable.
This rule is helpful when converting from an imperative code style to a functional one.
For performance reasons, eslint-plugin-functional does not check implicit return types. So for example this function will return a mutable array but will not be detected:
function foo() {
return [1, 2, 3];
}
To avoid this situation you can enable @typescript-eslint/explicit-function-return-type
. Now the above function is forced to declare the return type and the mutability will be detected.
How to contribute
For new features file an issue. For bugs, file an issue and optionally file a PR with a failing test.
How to develop
To execute the tests run yarn test
.
To learn about ESLint plugin development see the relevant section of the ESLint docs. You can also checkout the typescript-eslint repo which has some more information specific to TypeScript.
In order to know which AST nodes are created for a snippet of TypeScript code you can use AST explorer with options JavaScript and @typescript-eslint/parser.
How to publish
yarn version --patch
yarn version --minor
yarn version --major
Prior work
This project started off as a port of tslint-immutable which was originally inspired by eslint-plugin-immutable.