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": [
  "extends": [
  "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