跳至主要内容

prefer-readonly-parameter-types

要求函数参数类型为 readonly 以防止意外修改输入。

💭

此规则需要 类型信息 才能运行。

修改函数参数会导致难以理解、难以调试的行为。虽然很容易隐式记住不要修改函数参数,但将参数显式类型化为 readonly 为使用者提供了明确的契约。此契约使使用者更容易推断函数是否有副作用。

此规则允许您强制函数参数解析为 readonly 类型。如果类型为

  • 原始类型 (stringnumberbooleansymbol 或枚举),
  • 函数签名类型,
  • 这是一个只读数组类型,其元素类型被视为只读。
  • 这是一个只读元组类型,其所有元素都被视为只读。
  • 这是一个对象类型,其所有属性都被标记为只读,其所有值都被视为只读。
.eslintrc.cjs
module.exports = {
"rules": {
"@typescript-eslint/prefer-readonly-parameter-types": "error"
}
};

在游乐场中尝试此规则 ↗

示例

function array1(arg: string[]) {} // array is not readonly
function array2(arg: readonly string[][]) {} // 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<T> 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<T> instead

interface CustomArrayType extends ReadonlyArray<string> {
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<number[]>) {} // not all types are readonly

// rule also checks function types
interface Foo {
(arg: string[]): void;
}
interface Foo {
new (arg: string[]): void;
}
const x = { foo(arg: string[]): void {} };
function foo(arg: string[]);
type Foo = (arg: string[]) => void;
interface Foo {
foo(arg: string[]): void;
}
在游乐场中打开

选项

此规则接受以下选项

type Options = [
{
allow?: (
| {
from: 'file';
name: [string, ...string[]] | string;
path?: string;
}
| {
from: 'lib';
name: [string, ...string[]] | string;
}
| {
from: 'package';
name: [string, ...string[]] | string;
package: string;
}
| string
)[];
checkParameterProperties?: boolean;
ignoreInferredTypes?: boolean;
treatMethodsAsReadonly?: boolean;
},
];

const defaultOptions: Options = [
{
allow: [],
checkParameterProperties: true,
ignoreInferredTypes: false,
treatMethodsAsReadonly: false,
},
];

allow

某些复杂类型不容易被设为只读,例如 HTMLElement 类型或 @types/jquery 中的 JQueryStatic 类型。此选项允许您全局禁用对这些类型的报告。

此选项接受一个类型说明符数组以忽略。数组中的每个项目必须具有以下形式之一

  • 在文件中定义的类型({ from: "file", name: "Foo", path: "src/foo-file.ts" },其中 path 是相对于项目根目录的可选路径)
  • 来自默认库的类型({ from: "lib", name: "Foo" }
  • 来自包的类型({ from: "package", name: "Foo", package: "foo-lib" },这也适用于在类型包中定义的类型)。

此外,类型可以简单地定义为一个字符串,然后它将独立于其来源匹配该类型。

此规则的代码示例,其中

{
"allow": [
"$",
{ "from": "file", "name": "Foo" },
{ "from": "lib", "name": "HTMLElement" },
{ "from": "package", "name": "Bar", "package": "bar-lib" }
]
}
interface ThisIsMutable {
prop: string;
}

interface Wrapper {
sub: ThisIsMutable;
}

interface WrapperWithOther {
readonly sub: Foo;
otherProp: string;
}

// Incorrect because ThisIsMutable is not readonly
function fn1(arg: ThisIsMutable) {}

// Incorrect because Wrapper.sub is not readonly
function fn2(arg: Wrapper) {}

// Incorrect because WrapperWithOther.otherProp is not readonly and not in the allowlist
function fn3(arg: WrapperWithOther) {}
在游乐场中打开
import { Foo } from 'some-lib';
import { Bar } from 'incorrect-lib';

interface HTMLElement {
prop: string;
}

// Incorrect because Foo is not a local type
function fn1(arg: Foo) {}

// Incorrect because HTMLElement is not from the default library
function fn2(arg: HTMLElement) {}

// Incorrect because Bar is not from "bar-lib"
function fn3(arg: Bar) {}
在游乐场中打开

checkParameterProperties

此选项允许您启用或禁用参数属性的检查。由于参数属性在类上创建属性,因此可能不希望强制它们为只读。

此规则的代码示例,其中 {checkParameterProperties: true}

class Foo {
constructor(private paramProp: string[]) {}
}
在游乐场中打开

此规则的正确代码示例,其中 {checkParameterProperties: false}

class Foo {
constructor(
private paramProp1: string[],
private paramProp2: readonly string[],
) {}
}
在游乐场中打开

ignoreInferredTypes

此选项允许您忽略未显式指定类型的参数。这在外部依赖项指定具有可变参数的回调,并且手动注释回调的参数不可取的情况下可能很有用。

带有 {ignoreInferredTypes: true} 的此规则的代码示例

import { acceptsCallback, CallbackOptions } from 'external-dependency';

acceptsCallback((options: CallbackOptions) => {});
在游乐场中打开
external-dependency.d.ts
export interface CallbackOptions {
prop: string;
}
type Callback = (options: CallbackOptions) => void;
type AcceptsCallback = (callback: Callback) => void;

export const acceptsCallback: AcceptsCallback;

treatMethodsAsReadonly

此选项允许您将所有可变方法视为只读方法。当您从不重新分配方法时,这可能很有用。

带有 {treatMethodsAsReadonly: false} 的此规则的代码示例

type MyType = {
readonly prop: string;
method(): string; // note: this method is mutable
};
function foo(arg: MyType) {}
在游乐场中打开

带有 {treatMethodsAsReadonly: true} 的此规则的正确代码示例

type MyType = {
readonly prop: string;
method(): string; // note: this method is mutable
};
function foo(arg: MyType) {}
在游乐场中打开

何时不使用它

如果您的项目不尝试强制执行参数的强不变性保证,您可以避免使用此规则。

此规则对它认为可变的内容非常严格。许多自称为只读的类型被认为是可变的,因为它们具有可变属性,例如数组或元组。为了解决这些限制,您可能需要使用该规则的选项。特别是,allow 选项 可以显式地将类型标记为只读。


类型检查的 lint 规则比传统的 lint 规则更强大,但也需要配置 类型检查的 lint。如果您在启用类型检查规则后遇到性能下降,请参阅 性能故障排除

资源