eslint-config-typed 3.5.2 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/configs/browser.mjs +1 -0
- package/dist/configs/browser.mjs.map +1 -1
- package/dist/configs/cypress.mjs +1 -0
- package/dist/configs/cypress.mjs.map +1 -1
- package/dist/configs/jest.mjs +1 -0
- package/dist/configs/jest.mjs.map +1 -1
- package/dist/configs/nodejs.mjs +1 -0
- package/dist/configs/nodejs.mjs.map +1 -1
- package/dist/configs/playwright.mjs +1 -0
- package/dist/configs/playwright.mjs.map +1 -1
- package/dist/configs/plugins.d.mts +1 -1
- package/dist/configs/plugins.d.mts.map +1 -1
- package/dist/configs/plugins.mjs +3 -2
- package/dist/configs/plugins.mjs.map +1 -1
- package/dist/configs/react-base.mjs +1 -0
- package/dist/configs/react-base.mjs.map +1 -1
- package/dist/configs/testing-library.mjs +1 -0
- package/dist/configs/testing-library.mjs.map +1 -1
- package/dist/configs/typescript.d.mts.map +1 -1
- package/dist/configs/typescript.mjs +2 -0
- package/dist/configs/typescript.mjs.map +1 -1
- package/dist/configs/vitest.mjs +1 -0
- package/dist/configs/vitest.mjs.map +1 -1
- package/dist/entry-point.d.mts +1 -1
- package/dist/entry-point.d.mts.map +1 -1
- package/dist/entry-point.mjs +2 -1
- package/dist/entry-point.mjs.map +1 -1
- package/dist/index.mjs +2 -1
- package/dist/index.mjs.map +1 -1
- package/dist/plugins/index.d.mts +1 -1
- package/dist/plugins/index.d.mts.map +1 -1
- package/dist/plugins/index.mjs +1 -1
- package/dist/plugins/react-coding-style/rules/component-name.d.mts.map +1 -1
- package/dist/plugins/react-coding-style/rules/component-name.mjs.map +1 -1
- package/dist/plugins/react-coding-style/rules/import-style.d.mts.map +1 -1
- package/dist/plugins/react-coding-style/rules/import-style.mjs.map +1 -1
- package/dist/plugins/react-coding-style/rules/react-memo-props-argument-name.d.mts.map +1 -1
- package/dist/plugins/react-coding-style/rules/react-memo-props-argument-name.mjs.map +1 -1
- package/dist/plugins/react-coding-style/rules/react-memo-type-parameter.d.mts.map +1 -1
- package/dist/plugins/react-coding-style/rules/react-memo-type-parameter.mjs.map +1 -1
- package/dist/plugins/react-coding-style/rules/shared.d.mts.map +1 -1
- package/dist/plugins/react-coding-style/rules/shared.mjs.map +1 -1
- package/dist/plugins/strict-dependencies/rules/resolve-import-path.d.mts.map +1 -1
- package/dist/plugins/strict-dependencies/rules/resolve-import-path.mjs.map +1 -1
- package/dist/plugins/strict-dependencies/rules/strict-dependencies.d.mts.map +1 -1
- package/dist/plugins/strict-dependencies/rules/strict-dependencies.mjs.map +1 -1
- package/dist/plugins/total-functions/rules/no-hidden-type-assertions.d.mts.map +1 -1
- package/dist/plugins/total-functions/rules/no-hidden-type-assertions.mjs.map +1 -1
- package/dist/plugins/total-functions/rules/no-nested-fp-ts-effects.d.mts.map +1 -1
- package/dist/plugins/total-functions/rules/no-nested-fp-ts-effects.mjs.map +1 -1
- package/dist/plugins/total-functions/rules/no-partial-division.d.mts.map +1 -1
- package/dist/plugins/total-functions/rules/no-partial-division.mjs.map +1 -1
- package/dist/plugins/total-functions/rules/no-partial-url-constructor.d.mts.map +1 -1
- package/dist/plugins/total-functions/rules/no-partial-url-constructor.mjs.map +1 -1
- package/dist/plugins/total-functions/rules/no-premature-fp-ts-effects.d.mts.map +1 -1
- package/dist/plugins/total-functions/rules/no-premature-fp-ts-effects.mjs.map +1 -1
- package/dist/plugins/total-functions/rules/no-unsafe-mutable-readonly-assignment.d.mts.map +1 -1
- package/dist/plugins/total-functions/rules/no-unsafe-mutable-readonly-assignment.mjs.map +1 -1
- package/dist/plugins/total-functions/rules/no-unsafe-readonly-mutable-assignment.d.mts.map +1 -1
- package/dist/plugins/total-functions/rules/no-unsafe-readonly-mutable-assignment.mjs.map +1 -1
- package/dist/plugins/total-functions/rules/require-strict-mode.d.mts.map +1 -1
- package/dist/plugins/total-functions/rules/require-strict-mode.mjs.map +1 -1
- package/dist/plugins/total-functions/rules/unsafe-assignment-rule.d.mts.map +1 -1
- package/dist/plugins/total-functions/rules/unsafe-assignment-rule.mjs.map +1 -1
- package/dist/plugins/tree-shakable/rules/import-star.mjs.map +1 -1
- package/dist/plugins/ts-restrictions/index.d.mts +2 -0
- package/dist/plugins/ts-restrictions/index.d.mts.map +1 -0
- package/dist/plugins/ts-restrictions/index.mjs +2 -0
- package/dist/plugins/ts-restrictions/plugin.d.mts +3 -0
- package/dist/plugins/ts-restrictions/plugin.d.mts.map +1 -0
- package/dist/plugins/ts-restrictions/plugin.mjs +8 -0
- package/dist/plugins/ts-restrictions/plugin.mjs.map +1 -0
- package/dist/plugins/ts-restrictions/rules/index.d.mts.map +1 -0
- package/dist/plugins/ts-restrictions/rules/index.mjs +2 -0
- package/dist/plugins/ts-restrictions/rules/no-restricted-cast-name.d.mts +17 -0
- package/dist/plugins/ts-restrictions/rules/no-restricted-cast-name.d.mts.map +1 -0
- package/dist/plugins/ts-restrictions/rules/no-restricted-cast-name.mjs +171 -0
- package/dist/plugins/ts-restrictions/rules/no-restricted-cast-name.mjs.map +1 -0
- package/dist/plugins/{custom/rules/no-restricted-syntax2.d.mts → ts-restrictions/rules/no-restricted-syntax.d.mts} +1 -1
- package/dist/plugins/ts-restrictions/rules/no-restricted-syntax.d.mts.map +1 -0
- package/dist/plugins/{custom/rules/no-restricted-syntax2.mjs → ts-restrictions/rules/no-restricted-syntax.mjs} +1 -1
- package/dist/plugins/ts-restrictions/rules/no-restricted-syntax.mjs.map +1 -0
- package/dist/plugins/ts-restrictions/rules/rules.d.mts +14 -0
- package/dist/plugins/ts-restrictions/rules/rules.d.mts.map +1 -0
- package/dist/plugins/ts-restrictions/rules/rules.mjs +10 -0
- package/dist/plugins/ts-restrictions/rules/rules.mjs.map +1 -0
- package/dist/rules/eslint-import-rules.mjs +1 -1
- package/dist/rules/eslint-import-rules.mjs.map +1 -1
- package/dist/rules/eslint-stylistic-rules.d.mts +17 -1
- package/dist/rules/eslint-stylistic-rules.d.mts.map +1 -1
- package/dist/rules/eslint-stylistic-rules.mjs +27 -1
- package/dist/rules/eslint-stylistic-rules.mjs.map +1 -1
- package/dist/rules/eslint-ts-restrictions-rules.d.mts +5 -0
- package/dist/rules/eslint-ts-restrictions-rules.d.mts.map +1 -0
- package/dist/rules/eslint-ts-restrictions-rules.mjs +7 -0
- package/dist/rules/eslint-ts-restrictions-rules.mjs.map +1 -0
- package/dist/rules/index.d.mts +1 -0
- package/dist/rules/index.d.mts.map +1 -1
- package/dist/rules/index.mjs +1 -0
- package/dist/rules/index.mjs.map +1 -1
- package/dist/types/define-known-rules.d.mts +2 -2
- package/dist/types/define-known-rules.d.mts.map +1 -1
- package/dist/types/flat-config.d.mts.map +1 -1
- package/dist/types/rules/eslint-ts-restrictions-rules.d.mts +150 -0
- package/dist/types/rules/eslint-ts-restrictions-rules.d.mts.map +1 -0
- package/dist/types/rules/eslint-ts-restrictions-rules.mjs +2 -0
- package/dist/types/rules/eslint-ts-restrictions-rules.mjs.map +1 -0
- package/dist/types/rules/index.d.mts +1 -1
- package/dist/types/rules/index.d.mts.map +1 -1
- package/package.json +1 -1
- package/src/configs/plugins.mts +4 -3
- package/src/configs/typescript.mts +2 -0
- package/src/entry-point.mts +1 -1
- package/src/plugins/index.mts +1 -1
- package/src/plugins/react-coding-style/rules/component-name.mts +1 -0
- package/src/plugins/react-coding-style/rules/import-style.mts +1 -0
- package/src/plugins/react-coding-style/rules/react-memo-props-argument-name.mts +1 -0
- package/src/plugins/react-coding-style/rules/react-memo-type-parameter.mts +4 -0
- package/src/plugins/react-coding-style/rules/shared.mts +1 -0
- package/src/plugins/strict-dependencies/rules/resolve-import-path.mts +2 -0
- package/src/plugins/strict-dependencies/rules/resolve-import-path.test.mts +6 -0
- package/src/plugins/strict-dependencies/rules/strict-dependencies.mts +4 -0
- package/src/plugins/strict-dependencies/rules/strict-dependencies.test.mts +2 -0
- package/src/plugins/total-functions/rules/no-enums.test.mts +4 -3
- package/src/plugins/total-functions/rules/no-hidden-type-assertions.mts +3 -0
- package/src/plugins/total-functions/rules/no-hidden-type-assertions.test.mts +32 -31
- package/src/plugins/total-functions/rules/no-nested-fp-ts-effects.mts +1 -0
- package/src/plugins/total-functions/rules/no-nested-fp-ts-effects.test.mts +28 -27
- package/src/plugins/total-functions/rules/no-partial-array-reduce.test.mts +25 -24
- package/src/plugins/total-functions/rules/no-partial-division.mts +3 -0
- package/src/plugins/total-functions/rules/no-partial-division.test.mts +22 -21
- package/src/plugins/total-functions/rules/no-partial-string-normalize.test.mts +16 -15
- package/src/plugins/total-functions/rules/no-partial-url-constructor.mts +4 -0
- package/src/plugins/total-functions/rules/no-partial-url-constructor.test.mts +10 -9
- package/src/plugins/total-functions/rules/no-premature-fp-ts-effects.mts +1 -0
- package/src/plugins/total-functions/rules/no-premature-fp-ts-effects.test.mts +14 -13
- package/src/plugins/total-functions/rules/no-unsafe-mutable-readonly-assignment.mts +1 -0
- package/src/plugins/total-functions/rules/no-unsafe-mutable-readonly-assignment.test.mts +483 -482
- package/src/plugins/total-functions/rules/no-unsafe-readonly-mutable-assignment.mts +1 -0
- package/src/plugins/total-functions/rules/no-unsafe-readonly-mutable-assignment.test.mts +334 -333
- package/src/plugins/total-functions/rules/no-unsafe-type-assertion.test.mts +7 -6
- package/src/plugins/total-functions/rules/require-strict-mode.mts +1 -0
- package/src/plugins/total-functions/rules/unsafe-assignment-rule.mts +4 -0
- package/src/plugins/tree-shakable/rules/import-star.mts +8 -0
- package/src/plugins/tree-shakable/rules/import-star.test.mts +63 -62
- package/src/plugins/ts-restrictions/index.mts +1 -0
- package/src/plugins/ts-restrictions/plugin.mts +6 -0
- package/src/plugins/ts-restrictions/rules/no-restricted-cast-name.mts +234 -0
- package/src/plugins/ts-restrictions/rules/no-restricted-cast-name.test.mts +310 -0
- package/src/plugins/ts-restrictions/rules/rules.mts +8 -0
- package/src/rules/eslint-import-rules.mts +2 -2
- package/src/rules/eslint-stylistic-rules.mts +36 -2
- package/src/rules/eslint-ts-restrictions-rules.mts +6 -0
- package/src/rules/index.mts +1 -0
- package/src/types/define-known-rules.mts +3 -3
- package/src/types/flat-config.mts +0 -1
- package/src/types/rules/eslint-ts-restrictions-rules.mts +174 -0
- package/src/types/rules/index.mts +1 -1
- package/dist/plugins/custom/custom.d.mts +0 -3
- package/dist/plugins/custom/custom.d.mts.map +0 -1
- package/dist/plugins/custom/custom.mjs +0 -8
- package/dist/plugins/custom/custom.mjs.map +0 -1
- package/dist/plugins/custom/index.d.mts +0 -2
- package/dist/plugins/custom/index.d.mts.map +0 -1
- package/dist/plugins/custom/index.mjs +0 -2
- package/dist/plugins/custom/rules/index.d.mts.map +0 -1
- package/dist/plugins/custom/rules/index.mjs +0 -2
- package/dist/plugins/custom/rules/no-restricted-syntax2.d.mts.map +0 -1
- package/dist/plugins/custom/rules/no-restricted-syntax2.mjs.map +0 -1
- package/dist/plugins/custom/rules/rules.d.mts +0 -4
- package/dist/plugins/custom/rules/rules.d.mts.map +0 -1
- package/dist/plugins/custom/rules/rules.mjs +0 -8
- package/dist/plugins/custom/rules/rules.mjs.map +0 -1
- package/dist/types/rules/eslint-custom-rules.d.mts +0 -62
- package/dist/types/rules/eslint-custom-rules.d.mts.map +0 -1
- package/dist/types/rules/eslint-custom-rules.mjs +0 -2
- package/dist/types/rules/eslint-custom-rules.mjs.map +0 -1
- package/src/plugins/custom/custom.mts +0 -6
- package/src/plugins/custom/index.mts +0 -1
- package/src/plugins/custom/rules/rules.mts +0 -6
- package/src/types/rules/eslint-custom-rules.mts +0 -76
- /package/dist/plugins/{custom → ts-restrictions}/index.mjs.map +0 -0
- /package/dist/plugins/{custom → ts-restrictions}/rules/index.d.mts +0 -0
- /package/dist/plugins/{custom → ts-restrictions}/rules/index.mjs.map +0 -0
- /package/src/plugins/{custom → ts-restrictions}/rules/index.mts +0 -0
- /package/src/plugins/{custom/rules/no-restricted-syntax2.mts → ts-restrictions/rules/no-restricted-syntax.mts} +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import parser from '@typescript-eslint/parser';
|
|
2
2
|
import { RuleTester } from '@typescript-eslint/rule-tester';
|
|
3
3
|
import { AST_NODE_TYPES } from '@typescript-eslint/utils';
|
|
4
|
+
import dedent from 'dedent';
|
|
4
5
|
import { noUnsafeMutableReadonlyAssignment } from './no-unsafe-mutable-readonly-assignment.mjs';
|
|
5
6
|
|
|
6
7
|
const ruleTester = new RuleTester({
|
|
@@ -22,180 +23,180 @@ ruleTester.run(
|
|
|
22
23
|
// zero parameters
|
|
23
24
|
{
|
|
24
25
|
filename: 'file.ts',
|
|
25
|
-
code: `
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
code: dedent`
|
|
27
|
+
const foo = () => {
|
|
28
|
+
return undefined;
|
|
29
|
+
};
|
|
30
|
+
foo();
|
|
31
|
+
`,
|
|
31
32
|
},
|
|
32
33
|
// zero parameters with extra argument (TypeScript will catch this so we don't flag it)
|
|
33
34
|
{
|
|
34
35
|
filename: 'file.ts',
|
|
35
|
-
code: `
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
code: dedent`
|
|
37
|
+
const foo = () => {
|
|
38
|
+
return undefined;
|
|
39
|
+
};
|
|
40
|
+
foo("");
|
|
41
|
+
`,
|
|
41
42
|
},
|
|
42
43
|
// non-object parameter
|
|
43
44
|
{
|
|
44
45
|
filename: 'file.ts',
|
|
45
|
-
code: `
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
code: dedent`
|
|
47
|
+
const foo = (a: string) => {
|
|
48
|
+
return undefined;
|
|
49
|
+
};
|
|
50
|
+
foo("a");
|
|
51
|
+
`,
|
|
51
52
|
},
|
|
52
53
|
// missing arguments (TypeScript will catch this so we don't flag it)
|
|
53
54
|
{
|
|
54
55
|
filename: 'file.ts',
|
|
55
|
-
code: `
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
code: dedent`
|
|
57
|
+
const foo = (a: string) => {
|
|
58
|
+
return undefined;
|
|
59
|
+
};
|
|
60
|
+
foo();
|
|
61
|
+
`,
|
|
61
62
|
},
|
|
62
63
|
// readonly -> readonly (nested object; type doesn't change)
|
|
63
64
|
{
|
|
64
65
|
filename: 'file.ts',
|
|
65
|
-
code: `
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
code: dedent`
|
|
67
|
+
type ReadonlyA = { readonly a: { readonly b: string } };
|
|
68
|
+
const func = (param: ReadonlyA): void => {
|
|
69
|
+
return undefined;
|
|
70
|
+
};
|
|
71
|
+
const readonlyA = { a: { b: "" } } as const;
|
|
72
|
+
func(readonlyA);
|
|
73
|
+
`,
|
|
73
74
|
},
|
|
74
75
|
// mutable -> mutable (type doesn't change)
|
|
75
76
|
{
|
|
76
77
|
filename: 'file.ts',
|
|
77
|
-
code: `
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
78
|
+
code: dedent`
|
|
79
|
+
type MutableA = {a: string};
|
|
80
|
+
const foo = (mut: MutableA) => {
|
|
81
|
+
mut.a = "whoops";
|
|
82
|
+
};
|
|
83
|
+
const mut: MutableA = { a: "" };
|
|
84
|
+
foo(mut);
|
|
85
|
+
`,
|
|
85
86
|
},
|
|
86
87
|
// object literal -> mutable (no reference to object retained)
|
|
87
88
|
{
|
|
88
89
|
filename: 'file.ts',
|
|
89
|
-
code: `
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
90
|
+
code: dedent`
|
|
91
|
+
type MutableA = {a: string};
|
|
92
|
+
const foo = (mut: MutableA) => {
|
|
93
|
+
mut.a = "whoops";
|
|
94
|
+
};
|
|
95
|
+
foo({ a: "" });
|
|
96
|
+
`,
|
|
96
97
|
},
|
|
97
98
|
// object literal -> mutable (mutable reference to property retained)
|
|
98
99
|
{
|
|
99
100
|
filename: 'file.ts',
|
|
100
|
-
code: `
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
101
|
+
code: dedent`
|
|
102
|
+
type MutableB = { b: string };
|
|
103
|
+
type MutableA = { readonly a: MutableB };
|
|
104
|
+
const func = (param: MutableA): void => {
|
|
105
|
+
return undefined;
|
|
106
|
+
};
|
|
107
|
+
const b: MutableB = { b: "" };
|
|
108
|
+
func({ a: b } as const);
|
|
109
|
+
`,
|
|
109
110
|
},
|
|
110
111
|
// object literal -> readonly (readonly reference to property retained)
|
|
111
112
|
{
|
|
112
113
|
filename: 'file.ts',
|
|
113
|
-
code: `
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
114
|
+
code: dedent`
|
|
115
|
+
type ReadonlyB = { readonly b: string };
|
|
116
|
+
type ReadonlyA = { readonly a: ReadonlyB };
|
|
117
|
+
const func = (param: ReadonlyA): void => {
|
|
118
|
+
return undefined;
|
|
119
|
+
};
|
|
120
|
+
const b: ReadonlyB = { b: "" } as const;
|
|
121
|
+
func({ a: b } as const);
|
|
122
|
+
`,
|
|
122
123
|
},
|
|
123
124
|
// object literal -> readonly (no reference to object or its property retained)
|
|
124
125
|
{
|
|
125
126
|
filename: 'file.ts',
|
|
126
|
-
code: `
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
127
|
+
code: dedent`
|
|
128
|
+
type ReadonlyB = { readonly b: string };
|
|
129
|
+
type ReadonlyA = { readonly a: ReadonlyB };
|
|
130
|
+
const func = (param: ReadonlyA): void => {
|
|
131
|
+
return undefined;
|
|
132
|
+
};
|
|
133
|
+
func({ a: { b: "" } } as const);
|
|
134
|
+
`,
|
|
134
135
|
},
|
|
135
136
|
// mutable (union) -> mutable
|
|
136
137
|
{
|
|
137
138
|
filename: 'file.ts',
|
|
138
|
-
code: `
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
139
|
+
code: dedent`
|
|
140
|
+
type MutableA = {a: string};
|
|
141
|
+
const foo = (mut: MutableA) => {
|
|
142
|
+
mut.a = "whoops";
|
|
143
|
+
};
|
|
144
|
+
const mut: MutableA | number = { a: "" };
|
|
145
|
+
foo(mut);
|
|
146
|
+
`,
|
|
146
147
|
},
|
|
147
148
|
// mutable -> mutable (union)
|
|
148
149
|
{
|
|
149
150
|
filename: 'file.ts',
|
|
150
|
-
code: `
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
151
|
+
code: dedent`
|
|
152
|
+
type MutableA = {a: string};
|
|
153
|
+
const foo = (mut: MutableA | number): void => {
|
|
154
|
+
return;
|
|
155
|
+
};
|
|
156
|
+
const mut: MutableA = { a: "" };
|
|
157
|
+
foo(mut);
|
|
158
|
+
`,
|
|
158
159
|
},
|
|
159
160
|
// multiple type signatures (readonly -> readonly)
|
|
160
161
|
{
|
|
161
162
|
filename: 'file.ts',
|
|
162
|
-
code: `
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
163
|
+
code: dedent`
|
|
164
|
+
type ReadonlyA = { readonly a: string };
|
|
165
|
+
|
|
166
|
+
export function func(a: number): number;
|
|
167
|
+
export function func(a: ReadonlyA): ReadonlyA;
|
|
168
|
+
export function func(a: any): any {
|
|
169
|
+
return a;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const readonlyA: ReadonlyA = { a: "" } as const;
|
|
173
|
+
func(readonlyA);
|
|
174
|
+
`,
|
|
174
175
|
},
|
|
175
176
|
// multiple type signatures (no matching signature)
|
|
176
177
|
// we don't bother flagging this because TypeScript itself will catch it
|
|
177
178
|
{
|
|
178
179
|
filename: 'file.ts',
|
|
179
|
-
code: `
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
180
|
+
code: dedent`
|
|
181
|
+
type ReadonlyA = { readonly a: string };
|
|
182
|
+
|
|
183
|
+
export function func(a: number): number;
|
|
184
|
+
export function func(a: string): string;
|
|
185
|
+
export function func(a: any): any {
|
|
186
|
+
return a;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const readonlyA: ReadonlyA = { a: "" } as const;
|
|
190
|
+
func(readonlyA);
|
|
191
|
+
`,
|
|
191
192
|
},
|
|
192
193
|
// readonly array concat.
|
|
193
194
|
{
|
|
194
195
|
filename: 'file.ts',
|
|
195
|
-
code: `
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
196
|
+
code: dedent`
|
|
197
|
+
const arr: ReadonlyArray<never> = [] as const;
|
|
198
|
+
const foo = arr.concat(arr, arr);
|
|
199
|
+
`,
|
|
199
200
|
},
|
|
200
201
|
// TODO: https://github.com/danielnixon/eslint-plugin-total-functions/issues/132
|
|
201
202
|
// // mutable array concat.
|
|
@@ -219,367 +220,367 @@ ruleTester.run(
|
|
|
219
220
|
// The `readonly`s align and `mutable`s align, so no surprising mutation can arise.
|
|
220
221
|
{
|
|
221
222
|
filename: 'file.ts',
|
|
222
|
-
code: `
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
223
|
+
code: dedent`
|
|
224
|
+
type MutableA = { a: string };
|
|
225
|
+
type ReadonlyB = { readonly b: string };
|
|
226
|
+
const func = (foo: MutableA | ReadonlyB): void => {
|
|
227
|
+
return;
|
|
228
|
+
};
|
|
229
|
+
const foo: MutableA | ReadonlyB = Date.now() > 0 ? { a: "" } : { b: "" } as const;
|
|
230
|
+
func(foo);
|
|
231
|
+
`,
|
|
231
232
|
},
|
|
232
233
|
// readonly array of readonly object -> readonly array of readonly object
|
|
233
234
|
{
|
|
234
235
|
filename: 'file.ts',
|
|
235
|
-
code: `
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
236
|
+
code: dedent`
|
|
237
|
+
type Obj = { readonly foo: string };
|
|
238
|
+
const foo = (a: ReadonlyArray<Obj>): number => a.length;
|
|
239
|
+
const arr: ReadonlyArray<Obj> = [] as const;
|
|
240
|
+
foo(arr);
|
|
241
|
+
`,
|
|
241
242
|
},
|
|
242
243
|
/** Assignment expressions */
|
|
243
244
|
// TODO
|
|
244
245
|
// Array safe mutable to readonly assignment with chained array operations.
|
|
245
246
|
{
|
|
246
247
|
filename: 'file.ts',
|
|
247
|
-
code: `
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
248
|
+
code: dedent`
|
|
249
|
+
const fooArray: readonly string[] = ["test"] as const;
|
|
250
|
+
const nextArray: readonly string[] = fooArray.filter((v) => v.length > 0).map((v) => v.length.toString());
|
|
251
|
+
`,
|
|
251
252
|
},
|
|
252
253
|
// Array safe mutable to readonly assignment. (array.filter)
|
|
253
254
|
{
|
|
254
255
|
filename: 'file.ts',
|
|
255
|
-
code: `
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
256
|
+
code: dedent`
|
|
257
|
+
const fooArray: readonly string[] = [""] as const;
|
|
258
|
+
const nextArray: readonly string[] = fooArray.filter((v) => v.length > 0);
|
|
259
|
+
`,
|
|
259
260
|
},
|
|
260
261
|
// Array safe mutable to readonly assignment. (array.map)
|
|
261
262
|
{
|
|
262
263
|
filename: 'file.ts',
|
|
263
|
-
code: `
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
264
|
+
code: dedent`
|
|
265
|
+
const fooArray: readonly string[] = ["test"] as const;
|
|
266
|
+
const nextArray: readonly string[] = fooArray.map((v) => v.length.toString());
|
|
267
|
+
`,
|
|
267
268
|
},
|
|
268
269
|
// Array safe mutable to readonly assignment. (array.concat)
|
|
269
270
|
{
|
|
270
271
|
filename: 'file.ts',
|
|
271
|
-
code: `
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
272
|
+
code: dedent`
|
|
273
|
+
const fooArray: readonly string[] = ["test-1"] as const;
|
|
274
|
+
const booArray: readonly string[] = ["test-2"] as const;
|
|
275
|
+
const nextArray: readonly string[] = fooArray.concat(booArray);
|
|
276
|
+
`,
|
|
276
277
|
},
|
|
277
278
|
// Array safe mutable to readonly assignment. (array.flatMap)
|
|
278
279
|
{
|
|
279
280
|
filename: 'file.ts',
|
|
280
|
-
code: `
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
281
|
+
code: dedent`
|
|
282
|
+
const fooArray: readonly string[][] = [["test"]] as const;
|
|
283
|
+
const nextArray: readonly string[] = fooArray.flatMap((v) => v.length.toString());
|
|
284
|
+
`,
|
|
284
285
|
},
|
|
285
286
|
// Array safe mutable to readonly assignment. (array.flat)
|
|
286
287
|
{
|
|
287
288
|
filename: 'file.ts',
|
|
288
|
-
code: `
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
289
|
+
code: dedent`
|
|
290
|
+
const fooArray: readonly string[][] = [["test"]] as const;
|
|
291
|
+
const nextArray: readonly string[] = fooArray.flat();
|
|
292
|
+
`,
|
|
292
293
|
},
|
|
293
294
|
// Array safe mutable to readonly assignment. (array.slice)
|
|
294
295
|
{
|
|
295
296
|
filename: 'file.ts',
|
|
296
|
-
code: `
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
297
|
+
code: dedent`
|
|
298
|
+
const fooArray: readonly string[] = ["test"] as const;
|
|
299
|
+
const nextArray: readonly string[] = fooArray.slice();
|
|
300
|
+
`,
|
|
300
301
|
},
|
|
301
302
|
/** Return statement */
|
|
302
303
|
// mutable -> mutable (function return)
|
|
303
304
|
{
|
|
304
305
|
filename: 'file.ts',
|
|
305
|
-
code: `
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
306
|
+
code: dedent`
|
|
307
|
+
type MutableA = { a: string };
|
|
308
|
+
type ReadonlyA = { readonly a: string };
|
|
309
|
+
|
|
310
|
+
function foo(): MutableA {
|
|
311
|
+
const ma: MutableA = { a: "" };
|
|
312
|
+
return ma;
|
|
313
|
+
}
|
|
314
|
+
`,
|
|
314
315
|
},
|
|
315
316
|
// readonly -> readonly (function return)
|
|
316
317
|
{
|
|
317
318
|
filename: 'file.ts',
|
|
318
|
-
code: `
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
319
|
+
code: dedent`
|
|
320
|
+
type MutableA = { a: string };
|
|
321
|
+
type ReadonlyA = { readonly a: string };
|
|
322
|
+
|
|
323
|
+
function foo(): ReadonlyA {
|
|
324
|
+
const ma: ReadonlyA = { a: "" } as const;
|
|
325
|
+
return ma;
|
|
326
|
+
}
|
|
327
|
+
`,
|
|
327
328
|
},
|
|
328
329
|
// mutable -> mutable (type changes)
|
|
329
330
|
{
|
|
330
331
|
filename: 'file.ts',
|
|
331
|
-
code: `
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
332
|
+
code: dedent`
|
|
333
|
+
type MutableA = { a: string };
|
|
334
|
+
type MutableB = { a: string | null };
|
|
335
|
+
const foo = (mut: MutableB): void => {
|
|
336
|
+
mut.a = null; // whoops
|
|
337
|
+
};
|
|
338
|
+
const mut: MutableA = { a: "" };
|
|
339
|
+
foo(mut);
|
|
340
|
+
`,
|
|
340
341
|
},
|
|
341
342
|
// tuple -> readonly array (nested objected property)
|
|
342
343
|
{
|
|
343
344
|
filename: 'file.ts',
|
|
344
|
-
code: `
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
345
|
+
code: dedent`
|
|
346
|
+
type Foo = {
|
|
347
|
+
readonly foo: ReadonlyArray<{
|
|
348
|
+
readonly a: string;
|
|
349
|
+
readonly b: string;
|
|
350
|
+
}>;
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
const f: Foo = {
|
|
354
|
+
foo: [{
|
|
355
|
+
a: "",
|
|
356
|
+
b: "",
|
|
357
|
+
}],
|
|
358
|
+
} as const;
|
|
359
|
+
`,
|
|
359
360
|
},
|
|
360
361
|
// mutable function return -> mutable function return (multiple call signatures).
|
|
361
362
|
{
|
|
362
363
|
filename: 'file.ts',
|
|
363
|
-
code: `
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
364
|
+
code: dedent`
|
|
365
|
+
type MutableA = { a: string };
|
|
366
|
+
|
|
367
|
+
interface MutableFunc {
|
|
368
|
+
(b: number): MutableA;
|
|
369
|
+
(b: string): MutableA;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const mf: MutableFunc = (b: number | string): MutableA => {
|
|
373
|
+
return { a: "" };
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
const rf: MutableFunc = mf;
|
|
377
|
+
`,
|
|
377
378
|
},
|
|
378
379
|
// readonly function return -> readonly function return (multiple call signatures).
|
|
379
380
|
{
|
|
380
381
|
filename: 'file.ts',
|
|
381
|
-
code: `
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
382
|
+
code: dedent`
|
|
383
|
+
type ReadonlyA = { readonly a: string };
|
|
384
|
+
|
|
385
|
+
interface ReadonlyFunc {
|
|
386
|
+
(b: number): ReadonlyA;
|
|
387
|
+
(b: string): ReadonlyA;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const mf: ReadonlyFunc = (b: number | string): ReadonlyA => {
|
|
391
|
+
return { a: "" } as const;
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
const rf: ReadonlyFunc = mf;
|
|
395
|
+
`,
|
|
395
396
|
},
|
|
396
397
|
// Return empty tuple from function that is declared to return some readonly array type.
|
|
397
398
|
{
|
|
398
399
|
filename: 'file.ts',
|
|
399
|
-
code: `
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
400
|
+
code: dedent`
|
|
401
|
+
type Foo = ReadonlyArray<{ readonly a: string }>;
|
|
402
|
+
const foo = (): Foo => {
|
|
403
|
+
return [] as const;
|
|
404
|
+
};
|
|
405
|
+
`,
|
|
405
406
|
},
|
|
406
407
|
// Return empty tuple from function that is declared to return empty tuple.
|
|
407
408
|
{
|
|
408
409
|
filename: 'file.ts',
|
|
409
|
-
code: `
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
410
|
+
code: dedent`
|
|
411
|
+
type Foo = readonly [];
|
|
412
|
+
const foo = (): Foo => {
|
|
413
|
+
return [] as const;
|
|
414
|
+
};
|
|
415
|
+
`,
|
|
415
416
|
},
|
|
416
417
|
// Return safe mutable array with chained array operations.
|
|
417
418
|
{
|
|
418
419
|
filename: 'file.ts',
|
|
419
|
-
code: `
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
420
|
+
code: dedent`
|
|
421
|
+
const fooArray: readonly string[] = ["test"] as const;
|
|
422
|
+
const nextArray = (): readonly string[] => fooArray.filter((v) => v.length > 0).map((v) => v.length.toString());
|
|
423
|
+
`,
|
|
423
424
|
},
|
|
424
425
|
// Return safe mutable array. (array.filter)
|
|
425
426
|
{
|
|
426
427
|
filename: 'file.ts',
|
|
427
|
-
code: `
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
428
|
+
code: dedent`
|
|
429
|
+
const fooArray: readonly string[] = [""] as const;
|
|
430
|
+
const nextArray = (): readonly string[] => fooArray.filter((v) => v.length > 0);
|
|
431
|
+
`,
|
|
431
432
|
},
|
|
432
433
|
// Return safe mutable array. (array.map)
|
|
433
434
|
{
|
|
434
435
|
filename: 'file.ts',
|
|
435
|
-
code: `
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
436
|
+
code: dedent`
|
|
437
|
+
const fooArray: readonly string[] = ["test"] as const;
|
|
438
|
+
const nextArray = (): readonly string[] => fooArray.map((v) => v.length.toString());
|
|
439
|
+
`,
|
|
439
440
|
},
|
|
440
441
|
// Return safe mutable array. (array.concat)
|
|
441
442
|
{
|
|
442
443
|
filename: 'file.ts',
|
|
443
|
-
code: `
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
444
|
+
code: dedent`
|
|
445
|
+
const fooArray: readonly string[] = ["test-1"] as const;
|
|
446
|
+
const booArray: readonly string[] = ["test-2"] as const;
|
|
447
|
+
const nextArray = (): readonly string[] => fooArray.concat(booArray);
|
|
448
|
+
`,
|
|
448
449
|
},
|
|
449
450
|
// Return safe mutable array. (array.flatMap)
|
|
450
451
|
{
|
|
451
452
|
filename: 'file.ts',
|
|
452
|
-
code: `
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
453
|
+
code: dedent`
|
|
454
|
+
const fooArray: readonly string[][] = [["test"]] as const;
|
|
455
|
+
const nextArray = (): readonly string[] => fooArray.flatMap((v) => v.length.toString());
|
|
456
|
+
`,
|
|
456
457
|
},
|
|
457
458
|
// Return safe mutable array. (array.flat)
|
|
458
459
|
{
|
|
459
460
|
filename: 'file.ts',
|
|
460
|
-
code: `
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
461
|
+
code: dedent`
|
|
462
|
+
const fooArray: readonly string[][] = [["test"]] as const;
|
|
463
|
+
const nextArray = (): readonly string[] => fooArray.flat();
|
|
464
|
+
`,
|
|
464
465
|
},
|
|
465
466
|
// Return safe mutable array. (array.slice)
|
|
466
467
|
{
|
|
467
468
|
filename: 'file.ts',
|
|
468
|
-
code: `
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
469
|
+
code: dedent`
|
|
470
|
+
const fooArray: readonly string[] = ["test"] as const;
|
|
471
|
+
const nextArray = (): readonly string[] => fooArray.slice();
|
|
472
|
+
`,
|
|
472
473
|
},
|
|
473
474
|
// Return safe mutable array.
|
|
474
475
|
{
|
|
475
476
|
filename: 'file.ts',
|
|
476
|
-
code: `
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
477
|
+
code: dedent`
|
|
478
|
+
const fooArray: readonly string[] = ["test"] as const;
|
|
479
|
+
const nextArray = (): readonly string[] => {
|
|
480
|
+
return fooArray.slice();
|
|
481
|
+
}
|
|
482
|
+
`,
|
|
482
483
|
},
|
|
483
484
|
// Yield safe mutable array.
|
|
484
485
|
{
|
|
485
486
|
filename: 'file.ts',
|
|
486
|
-
code: `
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
487
|
+
code: dedent`
|
|
488
|
+
const fooArray: readonly string[] = ["test"] as const;
|
|
489
|
+
export function* mySaga(): Generator<readonly string[]> {
|
|
490
|
+
yield fooArray.slice();
|
|
491
|
+
}
|
|
492
|
+
`,
|
|
492
493
|
},
|
|
493
494
|
// generators
|
|
494
495
|
{
|
|
495
496
|
filename: 'file.ts',
|
|
496
|
-
code: `
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
497
|
+
code: dedent`
|
|
498
|
+
export function* mySaga(): Generator<void> {
|
|
499
|
+
yield;
|
|
500
|
+
}
|
|
501
|
+
`,
|
|
501
502
|
},
|
|
502
503
|
// The five turtles.
|
|
503
504
|
// https://effectivetypescript.com/2021/05/06/unsoundness/#There-Are-Five-Turtles
|
|
504
505
|
// https://www.youtube.com/watch?v=wpgKd-rwnMw&t=1714s
|
|
505
506
|
{
|
|
506
507
|
filename: 'file.ts',
|
|
507
|
-
code: `
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
},
|
|
523
|
-
{
|
|
524
|
-
filename: 'file.ts',
|
|
525
|
-
code: `
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
508
|
+
code: dedent`
|
|
509
|
+
type Foo<U> = {
|
|
510
|
+
readonly a: U;
|
|
511
|
+
readonly b: Foo<Foo<U>>;
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
type Bar<U> = {
|
|
515
|
+
readonly a: U;
|
|
516
|
+
readonly b: Bar<Bar<U>>;
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
declare const foo: Foo<string>;
|
|
520
|
+
|
|
521
|
+
const bar: Bar<string> = foo;
|
|
522
|
+
`,
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
filename: 'file.ts',
|
|
526
|
+
code: dedent`
|
|
527
|
+
type Foo<U> = {
|
|
528
|
+
readonly a: U;
|
|
529
|
+
readonly b: () => Foo<Foo<U>>;
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
type Bar<U> = {
|
|
533
|
+
readonly a: U;
|
|
534
|
+
readonly b: () => Bar<Bar<U>>;
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
declare const foo: Foo<string>;
|
|
538
|
+
|
|
539
|
+
const bar: Bar<string> = foo;
|
|
540
|
+
`,
|
|
540
541
|
},
|
|
541
542
|
// Return empty array literal from function that is declared to return empty tuple.
|
|
542
543
|
{
|
|
543
544
|
filename: 'file.ts',
|
|
544
|
-
code: `
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
545
|
+
code: dedent`
|
|
546
|
+
const foo = (): readonly [] => {
|
|
547
|
+
return [];
|
|
548
|
+
};
|
|
549
|
+
`,
|
|
549
550
|
},
|
|
550
551
|
// https://github.com/danielnixon/eslint-plugin-total-functions/issues/741
|
|
551
552
|
{
|
|
552
553
|
filename: 'file.ts',
|
|
553
|
-
code: `
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
554
|
+
code: dedent`
|
|
555
|
+
type Foo<U> = {
|
|
556
|
+
b?: Foo<Foo<U>>;
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
const takesAFoo = <U>(foo: Foo<U>): void => {
|
|
560
|
+
return undefined;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
let foo: Foo<unknown> = { b: {} };
|
|
564
|
+
|
|
565
|
+
foo.b = foo;
|
|
566
|
+
|
|
567
|
+
takesAFoo(foo);
|
|
568
|
+
`,
|
|
568
569
|
},
|
|
569
570
|
{
|
|
570
571
|
filename: 'file.ts',
|
|
571
|
-
code: `
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
572
|
+
code: dedent`
|
|
573
|
+
declare const fooArray: readonly string[];
|
|
574
|
+
declare const takesReadonly: (a: readonly string[]) => void;
|
|
575
|
+
const nextArray: readonly string[] = takesReadonly(fooArray.slice());
|
|
576
|
+
`,
|
|
576
577
|
},
|
|
577
578
|
// Literal (mutable) assigned to immutable but safe because no surprising mutation can arise
|
|
578
579
|
{
|
|
579
580
|
filename: 'file.ts',
|
|
580
|
-
code: `
|
|
581
|
-
|
|
582
|
-
|
|
581
|
+
code: dedent`
|
|
582
|
+
const o: readonly string[] = [];
|
|
583
|
+
`,
|
|
583
584
|
},
|
|
584
585
|
],
|
|
585
586
|
invalid: [
|
|
@@ -587,10 +588,10 @@ ruleTester.run(
|
|
|
587
588
|
// TODO this should ideally be considered valid without requiring an `as const`.
|
|
588
589
|
{
|
|
589
590
|
filename: 'file.ts',
|
|
590
|
-
code: `
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
591
|
+
code: dedent`
|
|
592
|
+
type ReadonlyA = { readonly a: string };
|
|
593
|
+
const readonlyA: ReadonlyA = { a: "" };
|
|
594
|
+
`,
|
|
594
595
|
errors: [
|
|
595
596
|
{
|
|
596
597
|
messageId: 'errorStringGeneric',
|
|
@@ -621,15 +622,15 @@ ruleTester.run(
|
|
|
621
622
|
// mutable -> readonly
|
|
622
623
|
{
|
|
623
624
|
filename: 'file.ts',
|
|
624
|
-
code: `
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
625
|
+
code: dedent`
|
|
626
|
+
type MutableA = { a: string };
|
|
627
|
+
type ReadonlyA = Readonly<MutableA>;
|
|
628
|
+
const func = (param: ReadonlyA): void => {
|
|
629
|
+
return undefined;
|
|
630
|
+
};
|
|
631
|
+
const mutableA: MutableA = { a: "" };
|
|
632
|
+
func(mutableA);
|
|
633
|
+
`,
|
|
633
634
|
errors: [
|
|
634
635
|
{
|
|
635
636
|
messageId: 'errorStringGeneric',
|
|
@@ -640,15 +641,15 @@ ruleTester.run(
|
|
|
640
641
|
// mutable -> readonly (function return)
|
|
641
642
|
{
|
|
642
643
|
filename: 'file.ts',
|
|
643
|
-
code: `
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
644
|
+
code: dedent`
|
|
645
|
+
type MutableA = { a: string };
|
|
646
|
+
type ReadonlyA = { readonly a: string };
|
|
647
|
+
|
|
648
|
+
function foo(): ReadonlyA {
|
|
649
|
+
const ma: MutableA = { a: "" };
|
|
650
|
+
return ma;
|
|
651
|
+
}
|
|
652
|
+
`,
|
|
652
653
|
errors: [
|
|
653
654
|
{
|
|
654
655
|
messageId: 'errorStringGeneric',
|
|
@@ -659,13 +660,13 @@ ruleTester.run(
|
|
|
659
660
|
// mutable object prop -> readonly object prop (as part of intersection with non-object).
|
|
660
661
|
{
|
|
661
662
|
filename: 'file.ts',
|
|
662
|
-
code: `
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
663
|
+
code: dedent`
|
|
664
|
+
type MutableA = { a?: string } & number;
|
|
665
|
+
type ReadonlyA = { readonly a?: string } & number;
|
|
666
|
+
|
|
667
|
+
const ma: MutableA = 42;
|
|
668
|
+
const ra: ReadonlyA = ma;
|
|
669
|
+
`,
|
|
669
670
|
errors: [
|
|
670
671
|
{
|
|
671
672
|
messageId: 'errorStringGeneric',
|
|
@@ -676,17 +677,17 @@ ruleTester.run(
|
|
|
676
677
|
// mutable function value -> readonly.
|
|
677
678
|
{
|
|
678
679
|
filename: 'file.ts',
|
|
679
|
-
code: `
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
680
|
+
code: dedent`
|
|
681
|
+
type MyType = {
|
|
682
|
+
readonly filter: () => Record<string, string>;
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
const myValue: MyType = {
|
|
686
|
+
filter: () => ({ foo: "bar" }),
|
|
687
|
+
} as const;
|
|
688
|
+
|
|
689
|
+
const foo: Readonly<Record<string, string>> = myValue.filter();
|
|
690
|
+
`,
|
|
690
691
|
errors: [
|
|
691
692
|
{
|
|
692
693
|
messageId: 'errorStringGeneric',
|
|
@@ -697,17 +698,17 @@ ruleTester.run(
|
|
|
697
698
|
// mutable function array value -> readonly.
|
|
698
699
|
{
|
|
699
700
|
filename: 'file.ts',
|
|
700
|
-
code: `
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
701
|
+
code: dedent`
|
|
702
|
+
type MyType = {
|
|
703
|
+
readonly filter: () => string[];
|
|
704
|
+
};
|
|
705
|
+
|
|
706
|
+
const myValue: MyType = {
|
|
707
|
+
filter: () => (["bar"]),
|
|
708
|
+
} as const;
|
|
709
|
+
|
|
710
|
+
const foo: readonly string[] = myValue.filter();
|
|
711
|
+
`,
|
|
711
712
|
errors: [
|
|
712
713
|
{
|
|
713
714
|
messageId: 'errorStringGeneric',
|
|
@@ -717,18 +718,18 @@ ruleTester.run(
|
|
|
717
718
|
},
|
|
718
719
|
{
|
|
719
720
|
filename: 'file.ts',
|
|
720
|
-
code: `
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
721
|
+
code: dedent`
|
|
722
|
+
type MyReadonlyType = { readonly a: string };
|
|
723
|
+
type MyMutableType = { a: string };
|
|
724
|
+
|
|
725
|
+
const mutableVal: MyMutableType = { a: "" };
|
|
726
|
+
const readonlyArrayOfMutable: readonly MyMutableType[] = [mutableVal] as const;
|
|
727
|
+
|
|
728
|
+
const shouldBeImmutable: readonly MyReadonlyType[] =
|
|
729
|
+
readonlyArrayOfMutable.concat({
|
|
730
|
+
a: "",
|
|
731
|
+
});
|
|
732
|
+
`,
|
|
732
733
|
errors: [
|
|
733
734
|
{
|
|
734
735
|
messageId: 'errorStringGeneric',
|
|
@@ -739,15 +740,15 @@ ruleTester.run(
|
|
|
739
740
|
// yield expression (generator)
|
|
740
741
|
{
|
|
741
742
|
filename: 'file.ts',
|
|
742
|
-
code: `
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
743
|
+
code: dedent`
|
|
744
|
+
type MutableA = { a: string };
|
|
745
|
+
type ReadonlyA = { readonly a: string };
|
|
746
|
+
|
|
747
|
+
export function* mySaga(): Generator<ReadonlyA> {
|
|
748
|
+
const foo: MutableA = { a: "" };
|
|
749
|
+
yield foo;
|
|
750
|
+
}
|
|
751
|
+
`,
|
|
751
752
|
errors: [
|
|
752
753
|
{
|
|
753
754
|
messageId: 'errorStringGeneric',
|
|
@@ -758,66 +759,66 @@ ruleTester.run(
|
|
|
758
759
|
// deep(ish) type
|
|
759
760
|
{
|
|
760
761
|
filename: 'file.ts',
|
|
761
|
-
code: `
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
762
|
+
code: dedent`
|
|
763
|
+
type A = {
|
|
764
|
+
a: string;
|
|
765
|
+
};
|
|
766
|
+
|
|
767
|
+
type B = {
|
|
768
|
+
readonly a: A;
|
|
769
|
+
};
|
|
770
|
+
|
|
771
|
+
type C = {
|
|
772
|
+
readonly b: B;
|
|
773
|
+
};
|
|
774
|
+
|
|
775
|
+
type D = {
|
|
776
|
+
readonly c: C;
|
|
777
|
+
};
|
|
778
|
+
|
|
779
|
+
type E = {
|
|
780
|
+
readonly d: D;
|
|
781
|
+
};
|
|
782
|
+
|
|
783
|
+
type F = {
|
|
784
|
+
readonly e: E;
|
|
785
|
+
};
|
|
786
|
+
|
|
787
|
+
type G = {
|
|
788
|
+
readonly f: F;
|
|
789
|
+
};
|
|
790
|
+
|
|
791
|
+
type A2 = {
|
|
792
|
+
readonly a: string;
|
|
793
|
+
};
|
|
794
|
+
|
|
795
|
+
type B2 = {
|
|
796
|
+
readonly a: A2;
|
|
797
|
+
};
|
|
798
|
+
|
|
799
|
+
type C2 = {
|
|
800
|
+
readonly b: B2;
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
type D2 = {
|
|
804
|
+
readonly c: C2;
|
|
805
|
+
};
|
|
806
|
+
|
|
807
|
+
type E2 = {
|
|
808
|
+
readonly d: D2;
|
|
809
|
+
};
|
|
810
|
+
|
|
811
|
+
type F2 = {
|
|
812
|
+
readonly e: E2;
|
|
813
|
+
};
|
|
814
|
+
|
|
815
|
+
type G2 = {
|
|
816
|
+
readonly f: F2;
|
|
817
|
+
};
|
|
818
|
+
|
|
819
|
+
declare const g: G;
|
|
820
|
+
export const g2: G2 = g;
|
|
821
|
+
`,
|
|
821
822
|
errors: [
|
|
822
823
|
{
|
|
823
824
|
messageId: 'errorStringGeneric',
|