1 | "use strict";
|
2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3 | if (k2 === undefined) k2 = k;
|
4 | Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
5 | }) : (function(o, m, k, k2) {
|
6 | if (k2 === undefined) k2 = k;
|
7 | o[k2] = m[k];
|
8 | }));
|
9 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
10 | Object.defineProperty(o, "default", { enumerable: true, value: v });
|
11 | }) : function(o, v) {
|
12 | o["default"] = v;
|
13 | });
|
14 | var __importStar = (this && this.__importStar) || function (mod) {
|
15 | if (mod && mod.__esModule) return mod;
|
16 | var result = {};
|
17 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
18 | __setModuleDefault(result, mod);
|
19 | return result;
|
20 | };
|
21 | Object.defineProperty(exports, "__esModule", { value: true });
|
22 | const utils_1 = require("@typescript-eslint/utils");
|
23 | const tsutils_1 = require("tsutils");
|
24 | const ts = __importStar(require("typescript"));
|
25 | const util = __importStar(require("../util"));
|
26 | exports.default = util.createRule({
|
27 | name: 'no-unnecessary-type-assertion',
|
28 | meta: {
|
29 | docs: {
|
30 | description: 'Warns if a type assertion does not change the type of an expression',
|
31 | recommended: 'error',
|
32 | requiresTypeChecking: true,
|
33 | },
|
34 | fixable: 'code',
|
35 | messages: {
|
36 | unnecessaryAssertion: 'This assertion is unnecessary since it does not change the type of the expression.',
|
37 | contextuallyUnnecessary: 'This assertion is unnecessary since the receiver accepts the original type of the expression.',
|
38 | },
|
39 | schema: [
|
40 | {
|
41 | type: 'object',
|
42 | properties: {
|
43 | typesToIgnore: {
|
44 | type: 'array',
|
45 | items: {
|
46 | type: 'string',
|
47 | },
|
48 | },
|
49 | },
|
50 | },
|
51 | ],
|
52 | type: 'suggestion',
|
53 | },
|
54 | defaultOptions: [{}],
|
55 | create(context, [options]) {
|
56 | const sourceCode = context.getSourceCode();
|
57 | const parserServices = util.getParserServices(context);
|
58 | const checker = parserServices.program.getTypeChecker();
|
59 | const compilerOptions = parserServices.program.getCompilerOptions();
|
60 | |
61 |
|
62 |
|
63 |
|
64 | function couldBeTupleType(type) {
|
65 | const properties = type.getProperties();
|
66 | if (properties.length === 0) {
|
67 | return false;
|
68 | }
|
69 | let i = 0;
|
70 | for (; i < properties.length; ++i) {
|
71 | const name = properties[i].name;
|
72 | if (String(i) !== name) {
|
73 | if (i === 0) {
|
74 |
|
75 | return false;
|
76 | }
|
77 | break;
|
78 | }
|
79 | }
|
80 | for (; i < properties.length; ++i) {
|
81 | if (String(+properties[i].name) === properties[i].name) {
|
82 | return false;
|
83 | }
|
84 | }
|
85 | return true;
|
86 | }
|
87 | |
88 |
|
89 |
|
90 | function isPossiblyUsedBeforeAssigned(node) {
|
91 | const declaration = util.getDeclaration(checker, node);
|
92 | if (!declaration) {
|
93 |
|
94 | return true;
|
95 | }
|
96 | if (
|
97 |
|
98 | (0, tsutils_1.isStrictCompilerOptionEnabled)(compilerOptions, 'strictNullChecks') &&
|
99 |
|
100 |
|
101 | (0, tsutils_1.isVariableDeclaration)(declaration) &&
|
102 |
|
103 | declaration.initializer === undefined &&
|
104 | declaration.exclamationToken === undefined &&
|
105 | declaration.type !== undefined) {
|
106 |
|
107 | const declarationType = checker.getTypeFromTypeNode(declaration.type);
|
108 | const type = util.getConstrainedTypeAtLocation(checker, node);
|
109 | if (declarationType === type) {
|
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 | return true;
|
116 | }
|
117 | }
|
118 | return false;
|
119 | }
|
120 | function isConstAssertion(node) {
|
121 | return (node.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
|
122 | node.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
|
123 | node.typeName.name === 'const');
|
124 | }
|
125 | return {
|
126 | TSNonNullExpression(node) {
|
127 | var _a;
|
128 | if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.AssignmentExpression &&
|
129 | node.parent.operator === '=') {
|
130 | if (node.parent.left === node) {
|
131 | context.report({
|
132 | node,
|
133 | messageId: 'contextuallyUnnecessary',
|
134 | fix(fixer) {
|
135 | return fixer.removeRange([
|
136 | node.expression.range[1],
|
137 | node.range[1],
|
138 | ]);
|
139 | },
|
140 | });
|
141 | }
|
142 |
|
143 |
|
144 |
|
145 |
|
146 | return;
|
147 | }
|
148 | const originalNode = parserServices.esTreeNodeToTSNodeMap.get(node);
|
149 | const type = util.getConstrainedTypeAtLocation(checker, originalNode.expression);
|
150 | if (!util.isNullableType(type)) {
|
151 | if (isPossiblyUsedBeforeAssigned(originalNode.expression)) {
|
152 | return;
|
153 | }
|
154 | context.report({
|
155 | node,
|
156 | messageId: 'unnecessaryAssertion',
|
157 | fix(fixer) {
|
158 | return fixer.removeRange([
|
159 | originalNode.expression.end,
|
160 | originalNode.end,
|
161 | ]);
|
162 | },
|
163 | });
|
164 | }
|
165 | else {
|
166 |
|
167 |
|
168 | const contextualType = util.getContextualType(checker, originalNode);
|
169 | if (contextualType) {
|
170 |
|
171 |
|
172 | const typeIncludesUndefined = util.isTypeFlagSet(type, ts.TypeFlags.Undefined);
|
173 | const typeIncludesNull = util.isTypeFlagSet(type, ts.TypeFlags.Null);
|
174 | const contextualTypeIncludesUndefined = util.isTypeFlagSet(contextualType, ts.TypeFlags.Undefined);
|
175 | const contextualTypeIncludesNull = util.isTypeFlagSet(contextualType, ts.TypeFlags.Null);
|
176 |
|
177 |
|
178 | const isValidUndefined = typeIncludesUndefined
|
179 | ? contextualTypeIncludesUndefined
|
180 | : true;
|
181 | const isValidNull = typeIncludesNull
|
182 | ? contextualTypeIncludesNull
|
183 | : true;
|
184 | if (isValidUndefined && isValidNull) {
|
185 | context.report({
|
186 | node,
|
187 | messageId: 'contextuallyUnnecessary',
|
188 | fix(fixer) {
|
189 | return fixer.removeRange([
|
190 | originalNode.expression.end,
|
191 | originalNode.end,
|
192 | ]);
|
193 | },
|
194 | });
|
195 | }
|
196 | }
|
197 | }
|
198 | },
|
199 | 'TSAsExpression, TSTypeAssertion'(node) {
|
200 | var _a;
|
201 | if (((_a = options.typesToIgnore) === null || _a === void 0 ? void 0 : _a.includes(sourceCode.getText(node.typeAnnotation))) ||
|
202 | isConstAssertion(node.typeAnnotation)) {
|
203 | return;
|
204 | }
|
205 | const originalNode = parserServices.esTreeNodeToTSNodeMap.get(node);
|
206 | const castType = checker.getTypeAtLocation(originalNode);
|
207 | if ((0, tsutils_1.isTypeFlagSet)(castType, ts.TypeFlags.Literal) ||
|
208 | ((0, tsutils_1.isObjectType)(castType) &&
|
209 | ((0, tsutils_1.isObjectFlagSet)(castType, ts.ObjectFlags.Tuple) ||
|
210 | couldBeTupleType(castType)))) {
|
211 |
|
212 |
|
213 | return;
|
214 | }
|
215 | const uncastType = checker.getTypeAtLocation(originalNode.expression);
|
216 | if (uncastType === castType) {
|
217 | context.report({
|
218 | node,
|
219 | messageId: 'unnecessaryAssertion',
|
220 | fix(fixer) {
|
221 | if (originalNode.kind === ts.SyntaxKind.TypeAssertionExpression) {
|
222 | const closingAngleBracket = sourceCode.getTokenAfter(node.typeAnnotation);
|
223 | return (closingAngleBracket === null || closingAngleBracket === void 0 ? void 0 : closingAngleBracket.value) === '>'
|
224 | ? fixer.removeRange([
|
225 | node.range[0],
|
226 | closingAngleBracket.range[1],
|
227 | ])
|
228 | : null;
|
229 | }
|
230 | return fixer.removeRange([
|
231 | node.expression.range[1] + 1,
|
232 | node.range[1],
|
233 | ]);
|
234 | },
|
235 | });
|
236 | }
|
237 |
|
238 | },
|
239 | };
|
240 | },
|
241 | });
|
242 |
|
\ | No newline at end of file |