diff --git a/.eslintrc.json b/.eslintrc.json
index bd6c7fc07..22394e841 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -126,6 +126,7 @@
"functional/no-expression-statement": "off",
"functional/no-let": "off",
"functional/no-loop-statement": "off",
+ "functional/no-mixed-type": "off",
"functional/no-return-void": "off",
"functional/no-this-expression": "off",
"functional/no-throw-statement": "off",
diff --git a/README.md b/README.md
index 798121699..a5308487c 100644
--- a/README.md
+++ b/README.md
@@ -186,12 +186,14 @@ The [below section](#supported-rules) gives details on which rules are enabled b
:see_no_evil: = `no-mutations` Ruleset.
-| Name | Description | :see_no_evil: | :hear_no_evil: | :speak_no_evil: | :wrench: | :blue_heart: |
-| -------------------------------------------------------------- | -------------------------------------------------------------------------- | :---------------------------------------------: | :--------------------------------------: | :----------------------------------------------: | :------: | :---------------: |
-| [`immutable-data`](./docs/rules/immutable-data.md) | Disallow mutating objects and arrays | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :blue_heart: |
-| [`no-let`](./docs/rules/no-let.md) | Disallow mutable variables | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | |
-| [`no-method-signature`](./docs/rules/no-method-signature.md) | Enforce property signatures with readonly modifiers over method signatures | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :thought_balloon: |
-| [`prefer-readonly-type`](./docs/rules/prefer-readonly-type.md) | Use readonly types and readonly modifiers where possible | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :wrench: | :thought_balloon: |
+| Name | Description | :see_no_evil: | :hear_no_evil: | :speak_no_evil: | :wrench: | :blue_heart: |
+| -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | :---------------------------------------------: | :--------------------------------------: | :----------------------------------------------: | :------: | :---------------: |
+| [`immutable-data`](./docs/rules/immutable-data.md) | Disallow mutating objects and arrays | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :blue_heart: |
+| [`no-let`](./docs/rules/no-let.md) | Disallow mutable variables | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | |
+| [`no-method-signature`](./docs/rules/no-method-signature.md) | Enforce property signatures with readonly modifiers over method signatures | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :thought_balloon: |
+| [`prefer-immutable-parameter-types`](./docs/rules/prefer-immutable-parameter-types.md) | Require function parameters to be typed as certain immutability | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :thought_balloon: |
+| [`prefer-readonly-type`](./docs/rules/prefer-readonly-type.md) | Use readonly types and readonly modifiers where possible | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :wrench: | :thought_balloon: |
+| [`type-declaration-immutability`](./docs/rules/type-declaration-immutability.md) | Enforce the immutability of types based on patterns | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :thought_balloon: |
### No Object-Orientation Rules
diff --git a/docs/rules/prefer-immutable-parameter-types.md b/docs/rules/prefer-immutable-parameter-types.md
new file mode 100644
index 000000000..7f1e68892
--- /dev/null
+++ b/docs/rules/prefer-immutable-parameter-types.md
@@ -0,0 +1,195 @@
+# Prefer immutable parameter types over mutable ones (prefer-immutable-parameter-types)
+
+Although other rules can be used to ensure parameter are not mutated, it is
+best to explicitly declare that the parameters are immutable.
+
+It is also worth noting that as immutable types are not assignable to mutable
+ones, users will not be able to pass something like a readonly array to a
+functional that wants a mutable array; even if the function does not actually
+mutate said array.
+
+## Rule Details
+
+This rule differs from the
+[@typescript-eslint/prefer-readonly-parameter-types](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/prefer-readonly-parameter-types.md)
+rule by the fact that it uses the
+[is-immutable-type](https://www.npmjs.com/package/is-immutable-type) library to
+calculated immutability. This library allows for more powerful and customizable
+immutability enforcements to be made.
+
+This rule is designed to replace the aforementioned rule.
+
+Examples of **incorrect** code for this rule:
+
+
+
+```ts
+/* eslint functional/prefer-immutable-parameter-types: "error" */
+
+function array1(arg: string[]) {} // array is not readonly
+function array2(arg: ReadonlyArray) {} // array element is not readonly
+function array3(arg: [string, number]) {} // tuple is not readonly
+function array4(arg: readonly [string[], number]) {} // tuple element is not readonly
+// the above examples work the same if you use ReadonlyArray instead
+
+function object1(arg: { prop: string }) {} // property is not readonly
+function object2(arg: { readonly prop: string; prop2: string }) {} // not all properties are readonly
+function object3(arg: { readonly prop: { prop2: string } }) {} // nested property is not readonly
+// the above examples work the same if you use Readonly instead
+
+interface CustomArrayType extends ReadonlyArray {
+ prop: string; // note: this property is mutable
+}
+function custom1(arg: CustomArrayType) {}
+
+interface CustomFunction {
+ (): void;
+ prop: string; // note: this property is mutable
+}
+function custom2(arg: CustomFunction) {}
+
+function union(arg: string[] | ReadonlyArray) {} // not all types are readonly
+
+// rule also checks function types
+interface Foo1 {
+ (arg: string[]): void;
+}
+interface Foo2 {
+ new (arg: string[]): void;
+}
+const x = { foo(arg: string[]): void; };
+function foo(arg: string[]);
+type Foo3 = (arg: string[]) => void;
+interface Foo4 {
+ foo(arg: string[]): void;
+}
+```
+
+Examples of **correct** code for this rule:
+
+
+
+```ts
+/* eslint functional/prefer-immutable-parameter-types: "error" */
+
+function array1(arg: ReadonlyArray) {}
+function array2(arg: ReadonlyArray>) {}
+function array3(arg: readonly [string, number]) {}
+function array4(arg: readonly [ReadonlyArray, number]) {}
+// the above examples work the same if you use ReadonlyArray instead
+
+function object1(arg: { readonly prop: string }) {}
+function object2(arg: { readonly prop: string; readonly prop2: string }) {}
+function object3(arg: { readonly prop: { readonly prop2: string } }) {}
+// the above examples work the same if you use Readonly instead
+
+interface CustomArrayType extends ReadonlyArray {
+ readonly prop: string;
+}
+function custom1(arg: Readonly) {}
+// interfaces that extend the array types are not considered arrays, and thus must be made readonly.
+
+interface CustomFunction {
+ (): void;
+ readonly prop: string;
+}
+function custom2(arg: CustomFunction) {}
+
+function union(arg: ReadonlyArray | ReadonlyArray) {}
+
+function primitive1(arg: string) {}
+function primitive2(arg: number) {}
+function primitive3(arg: boolean) {}
+function primitive4(arg: unknown) {}
+function primitive5(arg: null) {}
+function primitive6(arg: undefined) {}
+function primitive7(arg: any) {}
+function primitive8(arg: never) {}
+function primitive9(arg: string | number | undefined) {}
+
+function fnSig(arg: () => void) {}
+
+enum Foo { a, b }
+function enum1(arg: Foo) {}
+
+function symb1(arg: symbol) {}
+const customSymbol = Symbol('a');
+function symb2(arg: typeof customSymbol) {}
+
+// function types
+interface Foo1 {
+ (arg: ReadonlyArray): void;
+}
+interface Foo2 {
+ new (arg: ReadonlyArray): void;
+}
+const x = { foo(arg: ReadonlyArray): void; };
+function foo(arg: ReadonlyArray);
+type Foo3 = (arg: ReadonlyArray) => void;
+interface Foo4 {
+ foo(arg: ReadonlyArray): void;
+}
+```
+
+## Settings
+
+This rule can leverage shared settings to configure immutability settings.
+
+See the [immutability](./settings/immutability.md) docs.
+
+## Options
+
+This rule accepts an options object of the following type:
+
+```ts
+type Options = {
+ enforcement: "ReadonlyShallow" | "ReadonlyDeep" | "Immutable";
+}
+```
+
+The default options:
+
+```ts
+const defaults = {
+ enforcement: "ReadonlyDeep",
+}
+```
+
+### `enforcement`
+
+The level of immutability that should be enforced.
+
+**incorrect**:
+
+
+
+```ts
+/* eslint functional/prefer-immutable-parameter-types: ["error", { "enforcement": "Immutable" }] */
+
+function array(arg: ReadonlyArray) {} // ReadonlyArray is not immutable
+function set(arg: ReadonlySet) {} // ReadonlySet is not immutable
+function map(arg: ReadonlyMap) {} // ReadonlyMap is not immutable
+```
+
+**correct**:
+
+
+
+```ts
+/* eslint functional/prefer-immutable-parameter-types: ["error", { "enforcement": "Immutable" }] */
+
+function set(arg: Readonly>) {}
+function map(arg: Readonly>) {}
+function object(arg: Readonly<{ prop: string }>) {}
+```
+
+
+
+```ts
+/* eslint functional/prefer-immutable-parameter-types: ["error", { "enforcement": "ReadonlyShallow" }] */
+
+function array(arg: ReadonlyArray<{ foo: string; }>) {}
+function set(arg: ReadonlySet<{ foo: string; }>) {}
+function map(arg: ReadonlyMap<{ foo: string; }>) {}
+function object(arg: Readonly<{ prop: { foo: string; }; }>) {}
+```
diff --git a/docs/rules/settings/immutability.md b/docs/rules/settings/immutability.md
new file mode 100644
index 000000000..c2ec9a416
--- /dev/null
+++ b/docs/rules/settings/immutability.md
@@ -0,0 +1,41 @@
+# Using the `immutability` setting
+
+We are using the
+[is-immutable-type](https://www.npmjs.com/package/is-immutable-type) library to
+determine the immutability of types. This library can be configure for all rules
+at once using a shared setting.
+
+## Overrides
+
+For details see [the overrides
+section](https://github.com/RebeccaStevens/is-immutable-type#overrides) of
+[is-immutable-type](https://www.npmjs.com/package/is-immutable-type).
+
+### Example of configuring immutability overrides
+
+In this example, we are configuring
+[is-immutable-type](https://www.npmjs.com/package/is-immutable-type) to treat
+any readonly array (regardless of the syntax used) as immutable in the case
+where it was found to be deeply readonly. If it was only found to be shallowly
+readonly, then no override will be applied.
+
+```jsonc
+// .eslintrc.json
+{
+ // ...
+ "settings": {
+ "immutability": {
+ "overrides": [
+ {
+ "name": "ReadonlyArray",
+ "to": "Immutable",
+ "from": "ReadonlyDeep"
+ }
+ ]
+ }
+ },
+ "rules": {
+ // ...
+ }
+}
+```
diff --git a/docs/rules/type-declaration-immutability.md b/docs/rules/type-declaration-immutability.md
new file mode 100644
index 000000000..d204685c9
--- /dev/null
+++ b/docs/rules/type-declaration-immutability.md
@@ -0,0 +1,142 @@
+# Enforce a level of immutability for type declaration (type-declaration-immutability)
+
+Require type alias declarations and interfaces that imply some level of
+immutability to comply to it.
+
+## Rule Details
+
+This rule enforces rules on type immutability based on the type's name.
+
+For details on what the different levels of immutability mean, see [the
+immutability
+section](https://github.com/RebeccaStevens/is-immutable-type#immutability) of
+[is-immutable-type](https://www.npmjs.com/package/is-immutable-type).
+
+Examples of **incorrect** code for this rule:
+
+
+
+```ts
+/* eslint functional/type-declaration-immutability: "error" */
+
+type ReadonlyElement = {
+ id: number;
+ data: string[];
+};
+
+type ReadonlyDeepElement = Readonly<{
+ id: number;
+ data: string[];
+}>;
+
+type MutableElement = Readonly<{
+ id: number;
+ data: ReadonlyArray;
+}>;
+```
+
+Examples of **correct** code for this rule:
+
+
+
+```ts
+/* eslint functional/type-declaration-immutability: "error" */
+
+type ReadonlyElement = Readonly<{
+ id: number;
+ data: string[];
+}>;
+
+type ReadonlyDeepElement = Readonly<{
+ id: number;
+ data: ReadonlyArray;
+}>;
+
+type MutableElement = {
+ readonly id: number;
+ data: ReadonlyArray;
+};
+```
+
+## Settings
+
+This rule can leverage shared settings to configure immutability settings.
+
+See the [immutability](./settings/immutability.md) docs.
+
+## Options
+
+This rule accepts an options object of the following type:
+
+```ts
+type Options = {
+ rules: Array<{
+ identifier: string | string[];
+ immutability: "Mutable" | "ReadonlyShallow" | "ReadonlyDeep" | "Immutable";
+ comparator?: "Less" | "AtMost" | "Exactly" | "AtLeast" | "More";
+ }>;
+ ignoreInterfaces: boolean;
+ ignorePattern: string[] | string;
+}
+```
+
+The default options:
+
+```ts
+const defaults = {
+ rules: [
+ {
+ identifier: "I?Immutable.+",
+ immutability: "Immutable",
+ comparator: "AtLeast",
+ },
+ {
+ identifier: "I?ReadonlyDeep.+",
+ immutability: "ReadonlyDeep",
+ comparator: "AtLeast",
+ },
+ {
+ identifier: "I?Readonly.+",
+ immutability: "ReadonlyShallow",
+ comparator: "AtLeast",
+ },
+ {
+ identifier: "I?Mutable.+",
+ immutability: "Mutable",
+ comparator: "AtMost",
+ },
+ ],
+ ignoreInterfaces: false,
+}
+```
+
+### `rules`
+
+An array of rules to enforce immutability by.
+
+These rules should be sorted by precedence as each type declaration will only
+enforce the first matching rule to it.
+
+#### `identifier`
+
+A regex pattern or an array of regex patterns that are used to match against the
+name of the type declarations.
+
+#### `immutability`
+
+The level of immutability to compare against. This value will be compared to the
+calculated immutability using the `comparator`.
+
+#### `comparator`
+
+The comparator to use to compare the calculated immutability to the desired
+immutability. This can be thought of as `<`, `<=`, `==`, `>=` or `>`.
+
+### `ignoreInterfaces`
+
+A boolean to specify whether interfaces should be exempt from these rules.
+`false` by default.
+
+### `ignorePattern`
+
+See the [ignorePattern](./options/ignore-pattern.md) docs.