From eda6c89abaad63add59de8749d3646db92489b00 Mon Sep 17 00:00:00 2001 From: Rebecca Stevens Date: Mon, 15 Apr 2024 02:02:38 +1200 Subject: [PATCH] feat(prefer-immutable-types): allows for applying overrides to the options based on the type fix #800 --- docs/rules/prefer-immutable-types.md | 57 +++ eslint.config.js | 3 - src/rules/prefer-immutable-types.ts | 465 +++++++++++------- .../ts/parameters/invalid.ts | 31 ++ .../ts/parameters/valid.ts | 36 ++ 5 files changed, 398 insertions(+), 194 deletions(-) diff --git a/docs/rules/prefer-immutable-types.md b/docs/rules/prefer-immutable-types.md index 1f9db8df0..c468906bf 100644 --- a/docs/rules/prefer-immutable-types.md +++ b/docs/rules/prefer-immutable-types.md @@ -254,6 +254,37 @@ type Options = { Array<{ pattern: string; replace: string; message?: string }> >; }; + + overrides?: Array<{ + match: Array< + | { + from: "file"; + path?: string; + name?: string | string[]; + pattern?: RegExp | RegExp[]; + ignoreName?: string | string[]; + ignorePattern?: RegExp | RegExp[]; + } + | { + from: "lib"; + name?: string | string[]; + pattern?: RegExp | RegExp[]; + ignoreName?: string | string[]; + ignorePattern?: RegExp | RegExp[]; + } + | { + from: "package"; + package?: string; + name?: string | string[]; + pattern?: RegExp | RegExp[]; + ignoreName?: string | string[]; + ignorePattern?: RegExp | RegExp[]; + } + >; + options: Omit; + inherit?: boolean; + disable: boolean; + }>; }; ``` @@ -493,3 +524,29 @@ It allows for the ability to ignore violations based on the identifier (name) of This option takes a `RegExp` string or an array of `RegExp` strings. It allows for the ability to ignore violations based on the type (as written, with whitespace removed) of the node in question. + +### `overrides` + +Allows for applying overrides to the options based on the type's declaration. +This can be used to override the settings for types coming from 3rd party libraries. + +Note: Only the first matching override will be used. + +#### `overrides[n].specifiers` + +A specifier, or an array of specifiers to match the function type against. + +In the case of reference types, both the type and its generics will be recursively checked. +If any of them match, the specifier will be considered a match. + +#### `overrides[n].options` + +The options to use when a specifiers matches. + +#### `overrides[n].inherit` + +Inherit the root options? Default is `true`. + +#### `overrides[n].disable` + +If true, when a specifier matches, this rule will not be applied to the matching node. diff --git a/eslint.config.js b/eslint.config.js index 9797cbbc0..b52540def 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -23,9 +23,6 @@ export default rsEslint( rules: { // Some types say they have nonnullable properties, but they don't always. "ts/no-unnecessary-condition": "off", - - // Temp - "functional/prefer-immutable-types": "off", }, }, { diff --git a/src/rules/prefer-immutable-types.ts b/src/rules/prefer-immutable-types.ts index f2117e9d8..5134c1652 100644 --- a/src/rules/prefer-immutable-types.ts +++ b/src/rules/prefer-immutable-types.ts @@ -9,13 +9,19 @@ import { } from "@typescript-eslint/utils/ts-eslint"; import { deepmerge } from "deepmerge-ts"; import { Immutability } from "is-immutable-type"; +import { type Type, type TypeNode } from "typescript"; import { type IgnoreClassesOption, + type OverridableOptions, + type RawOverridableOptions, + getCoreOptions, + getCoreOptionsForType, ignoreClassesOptionSchema, shouldIgnoreClasses, shouldIgnoreInFunction, shouldIgnorePattern, + upgradeRawOverridableOptions, } from "#/options"; import { ruleNameScope } from "#/utils/misc"; import { type ESFunctionType } from "#/utils/node-types"; @@ -24,6 +30,7 @@ import { type RuleResult, createRule, getReturnTypesOfFunction, + getTypeDataOfNode, getTypeImmutabilityOfNode, getTypeImmutabilityOfType, isImplementationOfOverload, @@ -42,6 +49,8 @@ import { isTSTypePredicate, } from "#/utils/type-guards"; +import { overridableOptionsSchema } from "../utils/schemas"; + /** * The name of this rule. */ @@ -55,7 +64,8 @@ export const fullName = `${ruleNameScope}/${name}`; type RawEnforcement = | Exclude | "None" - | false; + | false + | undefined; type Option = IgnoreClassesOption & { enforcement: RawEnforcement; @@ -64,6 +74,20 @@ type Option = IgnoreClassesOption & { ignoreTypePattern?: string[] | string; }; +type CoreOptions = Option & { + parameters?: Partial