Skip to content

Commit 91010e8

Browse files
feat(eslint-plugin): [prefer-readonly-parameter-types] add ignoreInferredTypes option (#2668)
1 parent da71362 commit 91010e8

File tree

3 files changed

+108
-1
lines changed

3 files changed

+108
-1
lines changed

Diff for: packages/eslint-plugin/docs/rules/prefer-readonly-parameter-types.md

+52
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,12 @@ interface Foo {
124124
```ts
125125
interface Options {
126126
checkParameterProperties?: boolean;
127+
ignoreInferredTypes?: boolean;
127128
}
128129

129130
const defaultOptions: Options = {
130131
checkParameterProperties: true,
132+
ignoreInferredTypes: false,
131133
};
132134
```
133135

@@ -162,3 +164,53 @@ class Foo {
162164
) {}
163165
}
164166
```
167+
168+
### `ignoreInferredTypes`
169+
170+
This option allows you to ignore parameters which don't explicitly specify a type. 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.
171+
172+
Examples of **incorrect** code for this rule with `{ignoreInferredTypes: true}`:
173+
174+
```ts
175+
import { acceptsCallback, CallbackOptions } from 'external-dependency';
176+
177+
acceceptsCallback((options: CallbackOptions) => {});
178+
```
179+
180+
<details>
181+
<summary>external-dependency.d.ts</summary>
182+
183+
```ts
184+
export interface CallbackOptions {
185+
prop: string;
186+
}
187+
type Callback = (options: CallbackOptions) => void;
188+
type AcceptsCallback = (callback: Callback) => void;
189+
190+
export const acceptsCallback: AcceptsCallback;
191+
```
192+
193+
</details>
194+
195+
Examples of **correct** code for this rule with `{ignoreInferredTypes: true}`:
196+
197+
```ts
198+
import { acceptsCallback } from 'external-dependency';
199+
200+
acceceptsCallback(options => {});
201+
```
202+
203+
<details>
204+
<summary>external-dependency.d.ts</summary>
205+
206+
```ts
207+
export interface CallbackOptions {
208+
prop: string;
209+
}
210+
type Callback = (options: CallbackOptions) => void;
211+
type AcceptsCallback = (callback: Callback) => void;
212+
213+
export const acceptsCallback: AcceptsCallback;
214+
```
215+
216+
</details>

Diff for: packages/eslint-plugin/src/rules/prefer-readonly-parameter-types.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as util from '../util';
77
type Options = [
88
{
99
checkParameterProperties?: boolean;
10+
ignoreInferredTypes?: boolean;
1011
},
1112
];
1213
type MessageIds = 'shouldBeReadonly';
@@ -30,6 +31,9 @@ export default util.createRule<Options, MessageIds>({
3031
checkParameterProperties: {
3132
type: 'boolean',
3233
},
34+
ignoreInferredTypes: {
35+
type: 'boolean',
36+
},
3337
},
3438
},
3539
],
@@ -40,9 +44,11 @@ export default util.createRule<Options, MessageIds>({
4044
defaultOptions: [
4145
{
4246
checkParameterProperties: true,
47+
ignoreInferredTypes: false,
4348
},
4449
],
45-
create(context, [{ checkParameterProperties }]) {
50+
create(context, options) {
51+
const [{ checkParameterProperties, ignoreInferredTypes }] = options;
4652
const { esTreeNodeToTSNodeMap, program } = util.getParserServices(context);
4753
const checker = program.getTypeChecker();
4854

@@ -81,6 +87,11 @@ export default util.createRule<Options, MessageIds>({
8187
param.type === AST_NODE_TYPES.TSParameterProperty
8288
? param.parameter
8389
: param;
90+
91+
if (ignoreInferredTypes && actualParam.typeAnnotation == null) {
92+
continue;
93+
}
94+
8495
const tsNode = esTreeNodeToTSNodeMap.get(actualParam);
8596
const type = checker.getTypeAtLocation(tsNode);
8697
const isReadOnly = util.isTypeReadonly(checker, type);

Diff for: packages/eslint-plugin/tests/rules/prefer-readonly-parameter-types.test.ts

+44
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,24 @@ ruleTester.run('prefer-readonly-parameter-types', rule, {
247247
248248
const willNotCrash = (foo: Readonly<WithSymbol>) => {};
249249
`,
250+
{
251+
code: `
252+
type Callback<T> = (options: T) => void;
253+
254+
declare const acceptsCallback: <T>(callback: Callback<T>) => void;
255+
256+
interface CallbackOptions {
257+
prop: string;
258+
}
259+
260+
acceptsCallback<CallbackOptions>(options => {});
261+
`,
262+
options: [
263+
{
264+
ignoreInferredTypes: true,
265+
},
266+
],
267+
},
250268
],
251269
invalid: [
252270
// arrays
@@ -671,5 +689,31 @@ ruleTester.run('prefer-readonly-parameter-types', rule, {
671689
},
672690
],
673691
},
692+
{
693+
code: `
694+
type Callback<T> = (options: T) => void;
695+
696+
declare const acceptsCallback: <T>(callback: Callback<T>) => void;
697+
698+
interface CallbackOptions {
699+
prop: string;
700+
}
701+
702+
acceptsCallback<CallbackOptions>((options: CallbackOptions) => {});
703+
`,
704+
options: [
705+
{
706+
ignoreInferredTypes: true,
707+
},
708+
],
709+
errors: [
710+
{
711+
messageId: 'shouldBeReadonly',
712+
line: 10,
713+
column: 43,
714+
endColumn: 67,
715+
},
716+
],
717+
},
674718
],
675719
});

0 commit comments

Comments
 (0)