FP Lint
ESLint can help you enforce functional-first standards. Below is a functional eslint setup I like, followed by some explaination.
{
"root": true,
"parser": "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint",
"fp",
"prefer-let"
],
"extends": [
"airbnb",
"plugin:fp/recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"rules": {
"semi": ["error", "never"],
"prefer-const": "off",
"fp/no-let": "off",
"fp/no-arguments": "error",
"fp/no-class": "error",
"fp/no-delete": "error",
"fp/no-events": "error",
"fp/no-get-set": "error",
"fp/no-loops": "error",
"fp/no-mutating-assign": "error",
"fp/no-mutating-methods": "error",
"fp/no-mutation": "error",
"fp/no-nil": "error",
"fp/no-proxy": "error",
"fp/no-rest-parameters": "error",
"fp/no-this": "error",
"fp/no-throw": "error",
"fp/no-unused-expression": "error",
"fp/no-valueof-field": "error",
"no-var": "error",
"import/extensions": "off",
"import/no-unresolved": "off",
"react/prop-types": "off",
"react/react-in-jsx-scope": "off",
"react/jsx-filename-extension": "off",
"import/no-mutable-exports": "off",
"prefer-let/prefer-let": "error",
"implicit-arrow-linebreak": "off",
"arrow-parens": "off"
}
}
Disabling prefer-const
and fp/no-let
, and enabling of prefer-let/prefer-let
goes against popular JS doctrine. The prefer-const
rule is pretty standard in the JS community because const
forbids reassignment and reassignment is not idiomatic. However, a constant is the opposite of variable, which is what const
is usually used for. Semantically, let
is the modern keyword that replaces var
, except in instances where the value is known at compile time. These lint rules allow for use of let
, and only allow for const
at the top-level of a file. Reassignment is then forbidden by the lint rule fp/no-mutation
.
All of the other fp/*
settings emphatically enforce functional programming practices. They forbid mutation, forbid no-op functions (functions that do not return a value), and anything that relies on mutable state (like proxies and loops).
No semicolons ( semi: ["error", "never"]
) just makes things look more like a functional language.
Finally, turning off implicit-arrow-parens
reduces the need for brackets when you need to satisfy line length rules, and arrow-parens
are really just syntactic noise. Here's an example of how tabbing still makes the return clear, and parameters are clear without parenthesis because of the lambda arrow.
let double = (a) => {
return a * 2
}
let double = a =>
a * 2