跳至主要內容

偏好唯讀參數類型

需要函式參數指定為 readonly 類型,以防止輸入的意外變動。

💭

此規則需要 類型資訊 才能執行。

變動函式參數可能會導致令人困惑的難以除錯的行為。雖然很容易隱含地記住不要變更函式參數,但明確命名參數為唯讀會為使用者提供明確的合約。此合約讓使用者更容易推論函式是否有副作用。

此規則允許你強制函式參數解析為唯讀類型。唯讀類型

  • 是指
  • 基元類型 (字串數字布林符號 或列舉)、
  • 函式簽章類型、
  • 其元素類型視為唯讀的唯讀陣列類型。
  • 其元素都被視為唯讀的唯讀組類型。
.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

有些複雜類型無法輕易設定為唯讀,例如來自 @types/jqueryHTMLElement 類型或 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) {}
在試驗場開啟

不適合使用的情況

如果您的專案不嘗試執行強制性的強不可變性保證,則可以忽略這條規則。

此規則對於它視為可變的內容非常嚴格。許多宣告為 readonly 的類型皆視為可變,因為它們具有可變的屬性,例如陣列或組。若要解決這些限制,您可能需要使用該規則的選項。特別是,allow 選項 可以明確地將類型標記為 readonly。


類型檢查程式比傳統程式碼品質檢查更為強大,但類型檢查程式碼品質也需要設定 類型檢查程式碼品質。如果在啟用類型檢查規則後遇到效能下降問題,請參閱 效能問題解決

資源