Skip to content

Commit 91010e8

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

File tree

3 files changed

+108
-1
lines changed

3 files changed

+108
-1
lines changed

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

Lines changed: 52 additions & 0 deletions
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>

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

Lines changed: 12 additions & 1 deletion
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);

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

Lines changed: 44 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy