prefer-readonly-parameter-types
要求函数参数类型为
readonly
以防止意外修改输入。
此规则需要 类型信息 才能运行。
修改函数参数会导致难以理解、难以调试的行为。虽然很容易隐式记住不要修改函数参数,但将参数显式类型化为 readonly 为使用者提供了明确的契约。此契约使使用者更容易推断函数是否有副作用。
此规则允许您强制函数参数解析为 readonly 类型。如果类型为
- 原始类型 (
string
、number
、boolean
、symbol
或枚举), - 函数签名类型,
- 这是一个只读数组类型,其元素类型被视为只读。
- 这是一个只读元组类型,其所有元素都被视为只读。
- 这是一个对象类型,其所有属性都被标记为只读,其所有值都被视为只读。
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;
}
在游乐场中打开function 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;
}
在游乐场中打开选项
此规则接受以下选项
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) {}
在游乐场中打开interface 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) {}
在游乐场中打开import { 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) {}
在游乐场中打开import { Foo } from './foo';
// Works because Foo is still a local type - it has to be in the same package
function fn(arg: Foo) {}
在游乐场中打开checkParameterProperties
此选项允许您启用或禁用参数属性的检查。由于参数属性在类上创建属性,因此可能不希望强制它们为只读。
此规则的代码示例,其中 {checkParameterProperties: true}
- ❌ 错误
- ✅ 正确
此规则的正确代码示例,其中 {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;
import { acceptsCallback } from 'external-dependency';
acceptsCallback(options => {});
在游乐场中打开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) {}
在游乐场中打开type MyType = Readonly<{
prop: string;
method(): string;
}>;
function foo(arg: MyType) {}
type MyOtherType = {
readonly prop: string;
readonly method: () => string;
};
function bar(arg: MyOtherType) {}
在游乐场中打开带有 {treatMethodsAsReadonly: true}
的此规则的正确代码示例
type MyType = {
readonly prop: string;
method(): string; // note: this method is mutable
};
function foo(arg: MyType) {}
在游乐场中打开何时不使用它
如果您的项目不尝试强制执行参数的强不变性保证,您可以避免使用此规则。
此规则对它认为可变的内容非常严格。许多自称为只读的类型被认为是可变的,因为它们具有可变属性,例如数组或元组。为了解决这些限制,您可能需要使用该规则的选项。特别是,allow
选项 可以显式地将类型标记为只读。
类型检查的 lint 规则比传统的 lint 规则更强大,但也需要配置 类型检查的 lint。如果您在启用类型检查规则后遇到性能下降,请参阅 性能故障排除。