本文へスキップ

prefer-readonly-parameter-types

関数の引数を`readonly`として型指定し、意図しない変更を防ぎます。

💭

このルールには以下のものが要求されます。 型情報 を実行するには。

関数の引数を変更すると、混乱を招き、デバッグが困難になる可能性があります。関数の引数を変更しないように暗黙的に覚えているのは簡単ですが、引数を`readonly`として明示的に型指定することで、利用者に対して明確な契約を提供します。この契約により、関数が副作用を持つかどうかを、利用者がより簡単に判断できるようになります。

このルールを使用すると、関数の引数が読み取り専用型になるように強制できます。型は、以下の場合に読み取り専用とみなされます。

  • プリミティブ型(`string`、`number`、`boolean`、`symbol`、または列挙型)である場合、
  • 関数シグネチャ型である場合、
  • その要素型が読み取り専用とみなされる読み取り専用配列型である場合。
  • その要素がすべて読み取り専用とみなされる読み取り専用タプル型である場合。
  • そのプロパティがすべて読み取り専用としてマークされており、その値がすべて読み取り専用とみなされるオブジェクト型である場合。
.eslintrc.cjs
module.exports = {
"rules": {
"@typescript-eslint/prefer-readonly-parameter-types": "error"
}
};

Playgroundでこのルールを試してみてください ↗

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;
}
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;
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) {}
Playgroundで開く
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) {}
Playgroundで開く

checkParameterProperties

このオプションを使用すると、パラメータプロパティのチェックを有効または無効にできます。パラメータプロパティはクラスにプロパティを作成するため、読み取り専用にすることを強制しない方が望ましい場合があります。

{checkParameterProperties: true}を使用したこのルールのコード例

class Foo {
constructor(private paramProp: string[]) {}
}
Playgroundで開く

{checkParameterProperties: false}を使用したこのルールの**正しい**コード例

class Foo {
constructor(
private paramProp1: string[],
private paramProp2: readonly string[],
) {}
}
Playgroundで開く

ignoreInferredTypes

このオプションを使用すると、型を明示的に指定していないパラメータを無視できます。これは、外部の依存関係が変更可能なパラメータを持つコールバックを指定し、コールバックのパラメータを手動で注釈付けすることが望ましくない場合に望ましい場合があります。

{ignoreInferredTypes: true}を使用したこのルールのコード例

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

acceptsCallback((options: CallbackOptions) => {});
Playgroundで開く
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) {}
Playgroundで開く

{treatMethodsAsReadonly: true}を使用したこのルールの**正しい**コード例

type MyType = {
readonly prop: string;
method(): string; // note: this method is mutable
};
function foo(arg: MyType) {}
Playgroundで開く

使用しない場合

プロジェクトでパラメータの強い不変性の保証を強制しようとしていない場合は、このルールを避けることができます。

このルールは、変更可能とみなすものに関して非常に厳格です。読み取り専用として自身を記述する多くの型は、配列やタプルなどの変更可能なプロパティを持つため、変更可能とみなされます。これらの制限を回避するには、ルールのオプションを使用する必要がある場合があります。特に、`allow`オプションを使用して、型を読み取り専用として明示的にマークできます。


型チェックされたlintルールは、従来のlintルールよりも強力ですが、型チェックされたlintingの構成も必要です。型チェックされたルールを有効にした後にパフォーマンスの低下が発生する場合は、パフォーマンスに関するトラブルシューティングを参照してください。

リソース