{"id":13727179,"url":"https://github.com/danielnixon/eslint-plugin-total-functions","last_synced_at":"2025-04-09T17:22:01.884Z","repository":{"id":37067202,"uuid":"268045345","full_name":"danielnixon/eslint-plugin-total-functions","owner":"danielnixon","description":"An ESLint plugin to enforce the use of total functions (and prevent the use of partial functions) in TypeScript.","archived":false,"fork":false,"pushed_at":"2024-04-13T20:26:37.000Z","size":3167,"stargazers_count":86,"open_issues_count":39,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-04-14T10:53:55.769Z","etag":null,"topics":["eslint","eslint-plugin","functional-programming","partial-functions","type-safety","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danielnixon.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2020-05-30T09:07:50.000Z","updated_at":"2024-04-15T11:49:38.442Z","dependencies_parsed_at":"2023-10-15T04:48:49.811Z","dependency_job_id":"a2af29d7-1384-4109-a0ca-11804d1c77ac","html_url":"https://github.com/danielnixon/eslint-plugin-total-functions","commit_stats":{"total_commits":1051,"total_committers":8,"mean_commits":131.375,"dds":0.544243577545195,"last_synced_commit":"d249d78f35079ceb402380d2b20c7407db28b620"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielnixon%2Feslint-plugin-total-functions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielnixon%2Feslint-plugin-total-functions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielnixon%2Feslint-plugin-total-functions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielnixon%2Feslint-plugin-total-functions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danielnixon","download_url":"https://codeload.github.com/danielnixon/eslint-plugin-total-functions/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248075140,"owners_count":21043530,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["eslint","eslint-plugin","functional-programming","partial-functions","type-safety","typescript"],"created_at":"2024-08-03T01:03:43.114Z","updated_at":"2025-04-09T17:22:01.867Z","avatar_url":"https://github.com/danielnixon.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# TypeScript Total Functions - ESLint Plugin\n\n[![Build Status](https://github.com/danielnixon/eslint-plugin-total-functions/actions/workflows/node.js.yml/badge.svg)](https://github.com/danielnixon/eslint-plugin-total-functions/actions/workflows/node.js.yml)\n[![Type Coverage](https://img.shields.io/badge/dynamic/json.svg?label=type-coverage\u0026prefix=%E2%89%A5\u0026suffix=%\u0026query=$.typeCoverage.atLeast\u0026uri=https%3A%2F%2Fraw.githubusercontent.com%2Fdanielnixon%2Feslint-plugin-total-functions%2Fmaster%2Fpackage.json)](https://github.com/plantain-00/type-coverage)\n[![Test Coverage](https://codecov.io/gh/danielnixon/eslint-plugin-total-functions/branch/master/graph/badge.svg)](https://codecov.io/gh/danielnixon/eslint-plugin-total-functions)\n[![Mutation testing badge](https://img.shields.io/endpoint?style=flat\u0026url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fdanielnixon%2Feslint-plugin-total-functions%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/danielnixon/eslint-plugin-total-functions/master)\n[![Known Vulnerabilities](https://snyk.io/test/github/danielnixon/eslint-plugin-total-functions/badge.svg?targetFile=package.json)](https://snyk.io/test/github/danielnixon/eslint-plugin-total-functions?targetFile=package.json)\n[![Codacy Badge](https://app.codacy.com/project/badge/Grade/b228d6e8823946978bc94836211ee21b)](https://www.codacy.com/gh/danielnixon/eslint-plugin-total-functions/dashboard?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=danielnixon/eslint-plugin-total-functions\u0026amp;utm_campaign=Badge_Grade)\n[![npm](https://img.shields.io/npm/v/eslint-plugin-total-functions.svg)](https://www.npmjs.com/package/eslint-plugin-total-functions)\n\nAn ESLint plugin to enforce the use of total functions (and prevent the use of [partial functions](https://wiki.haskell.org/Partial_functions)) in TypeScript. If you like your types to tell the truth, this is the ESLint plugin for you.\n\n## Version Matrix\n\n| TypeScript       | ESLint  | eslint-plugin-total-functions | Suppported? |\n| :--------------: | :-----: | :---------------------------: | :---------: |\n|  5.0.2           | 8.36.0  | 7.0.0                         |             |\n|  4.9.5           | 8.35.0  | 6.2.0                         | No          |\n|  4.7.3           | 8.17.0  | 6.0.0                         | No          |\n|  4.5.4           | 8.5.0   | 5.0.0                         | No          |\n|  4.4.2           | 7.32.0  | 4.10.1                        | No          |\n|  4.3.5           | 7.30.0  | 4.8.0                         | No          |\n|  4.1.2           | 7.12.0  | 4.7.2                         | No          |\n|  4.0.2           | 7.9.0   | 3.3.0                         | No          |\n\n## Installation\n\n```sh\nyarn add --dev eslint-plugin-total-functions \\\n  @typescript-eslint/parser \\\n  eslint \\\n  typescript\n```\n\n## Setup\n\n### Option 1\n\nUse [eslint-config-typed-fp](https://github.com/danielnixon/eslint-config-typed-fp) which includes this plugin among others.\n\n### Option 2\n\n1. Turn on TypeScript's [strict mode](https://www.typescriptlang.org/tsconfig#strict) and [noUncheckedIndexedAccess](https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess) option.\n2. Set up [ESLint + TypeScript](https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/README.md).\n3. Turn on [eslint-plugin-functional](https://github.com/jonaskello/eslint-plugin-functional) (recommended). Its rules related to mutation and OO are more important than this plugin's rules and they'll help keep your types honest.\n4. Update your `.eslintrc.js`:\n\n```diff\nmodule.exports = {\n  parser: \"@typescript-eslint/parser\",\n  parserOptions: {\n    project: \"./tsconfig.json\",\n    ecmaVersion: 2018,\n    sourceType: \"module\"\n  },\n  extends: [\n+    \"plugin:total-functions/recommended\",\n  ],\n  plugins: [\n+    \"total-functions\",\n  ],\n};\n\n```\n\nAlternatively you can configure individual rules separately (see below).\n\n## Rules\n\n| Rule                                    | Recommended  | All   | Fixer? |\n| :-------------------------------------: | :----------: | :---: | :----: |\n|  require-strict-mode                    | ✅           | ✅    |        |\n|  no-unsafe-type-assertion               | ✅           | ✅    |        |\n|  no-unsafe-readonly-mutable-assignment  | ✅           | ✅    |        |\n|  no-unsafe-mutable-readonly-assignment  |              | ✅    |        |\n|  no-enums                               | ✅           | ✅    |        |\n|  no-partial-url-constructor             | ✅           | ✅    |        |\n|  no-partial-division                    | ✅           | ✅    |        |\n|  no-partial-string-normalize            | ✅           | ✅    |        |\n|  no-premature-fp-ts-effects             | ✅           | ✅    |        |\n|  no-nested-fp-ts-effects                |              | ✅    |        |\n|  no-partial-array-reduce                | ✅           | ✅    |        |\n|  no-hidden-type-assertions              |              | ✅    |        |\n\n### Deprecated rules\n\n* no-unsafe-optional-property-assignment\n* no-unsafe-enum-assignment (No longer required as of TypeScript 5, see https://devblogs.microsoft.com/typescript/announcing-typescript-5-0/#all-enums-are-union-enums)\n\n### total-functions/require-strict-mode\n\nThe world is a very strange place when [strict mode](https://www.typescriptlang.org/tsconfig#strict) is disabled. This rule enforces strict mode and [noUncheckedIndexedAccess](https://devblogs.microsoft.com/typescript/announcing-typescript-4-1-beta/#no-unchecked-indexed-access) mode (which is sadly not included under the strict umbrella).\n\n### total-functions/no-unsafe-type-assertion\n\nBans unsafe type assertions, for example:\n\n```typescript\ntype Foo = { readonly bar: number };\nconst foo = {} as Foo; // This compiles\nfoo.bar.toString(); // This explodes at runtime\n```\n\nThis is similar to the [consistent-type-assertions](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/consistent-type-assertions.md) rule from [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin), however:\n\n1. this rule is _weaker_ than `consistent-type-assertions` with its `assertionStyle` option set to `never` -- this rule will permit type assertions that it considers safe as opposed to blanket banning all type assertions, and\n2. this rule is _stronger_ than `consistent-type-assertions` with its `objectLiteralTypeAssertions` option set to `never`, for example:\n\n```typescript\ntype Foo = { readonly bar: number };\nconst foo = {};\nconst foo2 = foo as Foo; // Flagged by this rule, but not by consistent-type-assertions (unless you set assertionStyle to never)\nfoo2.bar.toString(); // This explodes at runtime\n```\n\nFor examples of type assertions that this rule considers valid and invalid, see [no-unsafe-type-assertion.test.ts](https://github.com/danielnixon/eslint-plugin-total-functions/blob/master/src/rules/no-unsafe-type-assertion.test.ts).\n\nSee [TypeScript issue #7481](https://github.com/microsoft/TypeScript/issues/7481) for a request to fix this at the language level.\n\n### total-functions/no-unsafe-readonly-mutable-assignment\n\nBans unsafe assignment of readonly values to mutable values (which can lead to surprising mutation in the readonly value). This includes passing readonly values as arguments to functions that expect mutable parameters.\n\nFor examples of assignment that this rule considers valid and invalid, see [no-unsafe-readonly-mutable-assignment.test.ts](https://github.com/danielnixon/eslint-plugin-total-functions/blob/master/src/rules/no-unsafe-readonly-mutable-assignment.test.ts).\n\nSee [TypeScript issue #13347](https://github.com/microsoft/TypeScript/issues/13347) for a request to fix this at the language level.\n\n### total-functions/no-unsafe-mutable-readonly-assignment\n\nThe inverse counterpart to no-unsafe-readonly-mutable-assignment. This rule bans unsafe assignment of mutable values to readonly values (which just like the inverse can lead to surprising mutation in the readonly value).\n\nThis rule is often noisy in practice so, unlike no-unsafe-readonly-mutable-assignment, is excluded from the `recommended` config.\n\nNote that the following is considered an assignment from mutable to readonly:\n\n```ts\n  type ReadonlyA = { readonly a: string };\n  const readonlyA: ReadonlyA = { a: \"\" };\n```\n\nThe solution is to append `as const` to the RHS:\n\n```ts\n  type ReadonlyA = { readonly a: string };\n  const readonlyA: ReadonlyA = { a: \"\" } as const;\n```\n\nFor examples of assignment that this rule considers valid and invalid, see [no-unsafe-mutable-readonly-assignment.test.ts](https://github.com/danielnixon/eslint-plugin-total-functions/blob/master/src/rules/no-unsafe-mutable-readonly-assignment.test.ts).\n\n### total-functions/no-enums\n\nEnums have a number of issues, including unsoundness issues (which are especially relevant here). This rule bans the declaration of enums entirely. Use an alternative such as a union of strings instead.\n\n### total-functions/no-unsafe-enum-assignment\n\n*Deprecated* No longer required as of TypeScript 5 (https://devblogs.microsoft.com/typescript/announcing-typescript-5-0/#all-enums-are-union-enums).\n\nIf you do use an enum (or are forced to by a library), this rule flags unsafe assignment that the TypeScript compiler (prior to version 5) permits. For example:\n\n```typescript\n  enum ZeroOrOne {\n    Zero = 0,\n    One = 1,\n  }\n\n  // This compiles (prior to TypeScript 5) but is flagged by no-unsafe-enum-assignment\n  const zeroOrOne: ZeroOrOne = 2;\n\n  // This is not flagged by no-unsafe-enum-assignment\n  const zeroOrOne: ZeroOrOne = ZeroOrOne.Zero;\n```\n\n### total-functions/no-partial-url-constructor\n\nThe URL constructor can throw (i.e. it is partial).\n\n```typescript\n// This compiles and foo appears to be a URL. It isn't.\nconst foo: URL = new URL(\"\"); // Throws TypeError [ERR_INVALID_URL]: Invalid URL\n```\n\nInstead, you should use a wrapper that catches that error and returns `URL | undefined` or similar (perhaps using an `Option` type).\n\nURL also happens to be mutable, which will be flagged by [prefer-immutable-types](https://github.com/eslint-functional/eslint-plugin-functional/blob/main/docs/rules/prefer-immutable-types.md). The [readonly-types](https://github.com/agiledigital/readonly-types) package provides a `readonlyURL` function that solves both of these issues.\n\n### total-functions/no-partial-division\n\nDivision by zero is undefined. That makes the division operator partial.\n\nIn the case of `number`, it results in `Infinity` (IEEE 754...).\n\nIn the case of `bigint` it throws a `RangeError`.\n\nThe latter is much more indisputably partial than the former.\n\n```\n\u003e 1 / 0\nInfinity\n\u003e 1n / 0n\nUncaught RangeError: Division by zero\n```\n\nThis rule flags division unless the denominator is provably non-zero. If you need division, you should wrap it in a wrapper that returns undefined when the denominator is zero. Alternatively, consider using branded types / refinements, such as https://github.com/gcanti/io-ts/blob/master/index.md#branded-types--refinements or https://gcanti.github.io/newtype-ts/modules/NonZero.ts.html\n\n# See Also\n* [TypeScript for Functional Programmers](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html)\n* https://github.com/danielnixon/eslint-config-typed-fp\n* https://github.com/agiledigital/readonly-types\n* https://github.com/eslint-functional/eslint-plugin-functional\n* https://github.com/RebeccaStevens/is-immutable-type\n* https://github.com/gcanti/fp-ts\n* https://github.com/plantain-00/type-coverage\n* https://github.com/immutable-js/immutable-js\n* https://github.com/shian15810/eslint-plugin-typescript-enum\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielnixon%2Feslint-plugin-total-functions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanielnixon%2Feslint-plugin-total-functions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielnixon%2Feslint-plugin-total-functions/lists"}