prefer-readonly-parameter-types
要求函数参数类型为
readonly
,以防止意外突变输入.
该规则需要 类型信息 才能运行,但这会带来性能方面的权衡。
改变函数参数可能会导致令人困惑且难以调试的行为。虽然很容易隐式记住不要修改函数参数,但显式将参数键入为只读可以为消费者提供明确的契约。该合约使消费者更容易推断函数是否有副作用。
¥Mutating function arguments can lead to confusing, hard to debug behavior. Whilst it's easy to implicitly remember to not modify function arguments, explicitly typing arguments as readonly provides clear contract to consumers. This contract makes it easier for a consumer to reason about if a function has side-effects.
此规则允许你强制将函数参数解析为只读类型。在以下情况下,类型被视为只读:
¥This rule allows you to enforce that function parameters resolve to readonly types. A type is considered readonly if:
-
它是一种原始类型(
string
、number
、boolean
、symbol
或枚举),¥it is a primitive type (
string
,number
,boolean
,symbol
, or an enum), -
它是一个函数签名类型,
¥it is a function signature type,
-
它是一个只读数组类型,其元素类型被视为只读。
¥it is a readonly array type whose element type is considered readonly.
-
它是一个只读元组类型,其元素都被视为只读。
¥it is a readonly tuple type whose elements are all considered readonly.
-
它是一种对象类型,其属性全部标记为只读,并且其值全部被视为只读。
¥it is an object type whose properties are all marked as readonly, and whose values are all considered readonly.
- 扁平配置
- 旧版配置
export default tseslint.config({
rules: {
"@typescript-eslint/prefer-readonly-parameter-types": "error"
}
});
module.exports = {
"rules": {
"@typescript-eslint/prefer-readonly-parameter-types": "error"
}
};
在线运行试试这个规则 ↗
示例
¥Examples
- ❌ 错误
- ✅ 正确
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;
}
Open in Playgroundfunction array1(arg: readonly string[]) {}
function array2(arg: readonly (readonly string[])[]) {}
function array3(arg: readonly [string, number]) {}
function array4(arg: readonly [readonly string[], number]) {}
// the above examples work the same if you use ReadonlyArray<T> 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<T> instead
interface CustomArrayType extends ReadonlyArray<string> {
readonly prop: string;
}
function custom1(arg: Readonly<CustomArrayType>) {}
// 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: readonly string[] | ReadonlyArray<number>) {}
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 enumArg(arg: Foo) {}
function symb1(arg: symbol) {}
const customSymbol = Symbol('a');
function symb2(arg: typeof customSymbol) {}
// function types
interface Foo {
(arg: readonly string[]): void;
}
interface Foo {
new (arg: readonly string[]): void;
}
const x = { foo(arg: readonly string[]): void {} };
function foo(arg: readonly string[]);
type Foo = (arg: readonly string[]) => void;
interface Foo {
foo(arg: readonly string[]): void;
}
Open in Playground选项
该规则接受以下选项:
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;
/** Whether to ignore parameters which don't explicitly specify a type. */
ignoreInferredTypes?: boolean;
/** 是否将所有可变方法视为只读。 */
treatMethodsAsReadonly?: boolean;
},
];
const defaultOptions: Options = [
{
allow: [],
checkParameterProperties: true,
ignoreInferredTypes: false,
treatMethodsAsReadonly: false,
},
];
¥Options
allow
要忽略的类型说明符数组。 Default: []
.
一些复杂类型不能轻易变为只读,例如 HTMLElement
类型或 @types/jquery
中的 JQueryStatic
类型。此选项允许你全局禁用此类类型的报告。
¥Some complex types cannot easily be made readonly, for example the HTMLElement
type or the JQueryStatic
type from @types/jquery
. This option allows you to globally disable reporting of such types.
此选项采用共享 TypeOrValueSpecifier
格式。
¥This option takes the shared TypeOrValueSpecifier
format.
带有此规则的代码示例:
¥Examples of code for this rule with:
{
"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) {}
Open in Playgroundimport { 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) {}
Open in Playgroundinterface Foo {
prop: string;
}
interface Wrapper {
readonly sub: Foo;
readonly otherProp: string;
}
// Works because Foo is allowed
function fn1(arg: Foo) {}
// Works even when Foo is nested somewhere in the type, with other properties still being checked
function fn2(arg: Wrapper) {}
Open in Playgroundimport { Bar } from 'bar-lib';
interface Foo {
prop: string;
}
// Works because Foo is a local type
function fn1(arg: Foo) {}
// Works because HTMLElement is from the default library
function fn2(arg: HTMLElement) {}
// Works because Bar is from "bar-lib"
function fn3(arg: Bar) {}
Open in Playgroundimport { Foo } from './foo';
// Works because Foo is still a local type - it has to be in the same package
function fn(arg: Foo) {}
Open in PlaygroundcheckParameterProperties
是否检查类参数属性。 Default: true
.
由于参数属性在类上创建属性,因此可能不希望强制它们为只读。
¥Because parameter properties create properties on the class, it may be undesirable to force them to be readonly.
带有 {checkParameterProperties: true}
的此规则的代码示例:
¥Examples of code for this rule with {checkParameterProperties: true}
:
- ❌ 错误
- ✅ 正确
class Foo {
constructor(private paramProp: string[]) {}
}
Open in Playgroundclass Foo {
constructor(private paramProp: readonly string[]) {}
}
Open in Playground带有 {checkParameterProperties: false}
的此规则的正确代码示例:
¥Examples of correct code for this rule with {checkParameterProperties: false}
:
class Foo {
constructor(
private paramProp1: string[],
private paramProp2: readonly string[],
) {}
}
Open in PlaygroundignoreInferredTypes
Whether to ignore parameters which don't explicitly specify a type. Default: false
.
在外部依赖指定具有可变参数的回调的情况下,这可能是理想的,并且手动注释回调的参数是不可取的。
¥This may be desirable in cases where an external dependency specifies a callback with mutable parameters, and manually annotating the callback's parameters is undesirable.
带有 {ignoreInferredTypes: true}
的此规则的代码示例:
¥Examples of code for this rule with {ignoreInferredTypes: true}
:
- ❌ 错误
- ✅ 正确
import { acceptsCallback, CallbackOptions } from 'external-dependency';
acceptsCallback((options: CallbackOptions) => {});
Open in Playgroundexternal-dependency.d.ts
export interface CallbackOptions {
prop: string;
}
type Callback = (options: CallbackOptions) => void;
type AcceptsCallback = (callback: Callback) => void;
export const acceptsCallback: AcceptsCallback;
import { acceptsCallback } from 'external-dependency';
acceptsCallback(options => {});
Open in Playgroundexternal-dependency.d.ts
export interface CallbackOptions {
prop: string;
}
type Callback = (options: CallbackOptions) => void;
type AcceptsCallback = (callback: Callback) => void;
export const acceptsCallback: AcceptsCallback;
treatMethodsAsReadonly
是否将所有可变方法视为只读。 Default: false
.
当你从不重新分配方法时,这可能是理想的。
¥This may be desirable when you are never reassigning methods.
带有 {treatMethodsAsReadonly: false}
的此规则的代码示例:
¥Examples of code for this rule with {treatMethodsAsReadonly: false}
:
- ❌ 错误
- ✅ 正确
type MyType = {
readonly prop: string;
method(): string; // note: this method is mutable
};
function foo(arg: MyType) {}
Open in Playgroundtype MyType = Readonly<{
prop: string;
method(): string;
}>;
function foo(arg: MyType) {}
type MyOtherType = {
readonly prop: string;
readonly method: () => string;
};
function bar(arg: MyOtherType) {}
Open in Playground带有 {treatMethodsAsReadonly: true}
的此规则的正确代码示例:
¥Examples of correct code for this rule with {treatMethodsAsReadonly: true}
:
type MyType = {
readonly prop: string;
method(): string; // note: this method is mutable
};
function foo(arg: MyType) {}
Open in Playground何时不使用它
¥When Not To Use It
如果你的项目不尝试强制执行参数的强不可变性保证,则可以避免此规则。
¥If your project does not attempt to enforce strong immutability guarantees of parameters, you can avoid this rule.
此规则允许你设置要在代码库中允许的指令注释。许多将自己描述为只读的类型被认为是可变的,因为它们具有可变属性,例如数组或元组。要解决这些限制,你可能需要使用规则的选项。特别是,allow
选项 可以明确将类型标记为只读。
¥This rule is very strict on what it considers mutable.
Many types that describe themselves as readonly are considered mutable because they have mutable properties such as arrays or tuples.
To work around these limitations, you might need to use the rule's options.
In particular, the allow
option can explicitly mark a type as readonly.
Type checked lint rules are more powerful than traditional lint rules, but also require configuring type checked linting.
See Troubleshooting > Linting with Type Information > Performance if you experience performance degradations after enabling type checked rules.