UNPKG

8.88 kBJavaScriptView Raw
1"use strict";
2var __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}));
9var __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});
14var __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};
21Object.defineProperty(exports, "__esModule", { value: true });
22/* eslint-disable @typescript-eslint/internal/prefer-ast-types-enum */
23const utils_1 = require("@typescript-eslint/utils");
24const util = __importStar(require("../util"));
25exports.default = util.createRule({
26 name: 'no-inferrable-types',
27 meta: {
28 type: 'suggestion',
29 docs: {
30 description: 'Disallows explicit type declarations for variables or parameters initialized to a number, string, or boolean',
31 recommended: 'error',
32 },
33 fixable: 'code',
34 messages: {
35 noInferrableType: 'Type {{type}} trivially inferred from a {{type}} literal, remove type annotation.',
36 },
37 schema: [
38 {
39 type: 'object',
40 properties: {
41 ignoreParameters: {
42 type: 'boolean',
43 },
44 ignoreProperties: {
45 type: 'boolean',
46 },
47 },
48 additionalProperties: false,
49 },
50 ],
51 },
52 defaultOptions: [
53 {
54 ignoreParameters: false,
55 ignoreProperties: false,
56 },
57 ],
58 create(context, [{ ignoreParameters, ignoreProperties }]) {
59 function isFunctionCall(init, callName) {
60 if (init.type === utils_1.AST_NODE_TYPES.ChainExpression) {
61 return isFunctionCall(init.expression, callName);
62 }
63 return (init.type === utils_1.AST_NODE_TYPES.CallExpression &&
64 init.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
65 init.callee.name === callName);
66 }
67 function isLiteral(init, typeName) {
68 return (init.type === utils_1.AST_NODE_TYPES.Literal && typeof init.value === typeName);
69 }
70 function isIdentifier(init, ...names) {
71 return (init.type === utils_1.AST_NODE_TYPES.Identifier && names.includes(init.name));
72 }
73 function hasUnaryPrefix(init, ...operators) {
74 return (init.type === utils_1.AST_NODE_TYPES.UnaryExpression &&
75 operators.includes(init.operator));
76 }
77 const keywordMap = {
78 [utils_1.AST_NODE_TYPES.TSBigIntKeyword]: 'bigint',
79 [utils_1.AST_NODE_TYPES.TSBooleanKeyword]: 'boolean',
80 [utils_1.AST_NODE_TYPES.TSNumberKeyword]: 'number',
81 [utils_1.AST_NODE_TYPES.TSNullKeyword]: 'null',
82 [utils_1.AST_NODE_TYPES.TSStringKeyword]: 'string',
83 [utils_1.AST_NODE_TYPES.TSSymbolKeyword]: 'symbol',
84 [utils_1.AST_NODE_TYPES.TSUndefinedKeyword]: 'undefined',
85 };
86 /**
87 * Returns whether a node has an inferrable value or not
88 */
89 function isInferrable(annotation, init) {
90 switch (annotation.type) {
91 case utils_1.AST_NODE_TYPES.TSBigIntKeyword: {
92 // note that bigint cannot have + prefixed to it
93 const unwrappedInit = hasUnaryPrefix(init, '-')
94 ? init.argument
95 : init;
96 return (isFunctionCall(unwrappedInit, 'BigInt') ||
97 (unwrappedInit.type === utils_1.AST_NODE_TYPES.Literal &&
98 'bigint' in unwrappedInit));
99 }
100 case utils_1.AST_NODE_TYPES.TSBooleanKeyword:
101 return (hasUnaryPrefix(init, '!') ||
102 isFunctionCall(init, 'Boolean') ||
103 isLiteral(init, 'boolean'));
104 case utils_1.AST_NODE_TYPES.TSNumberKeyword: {
105 const unwrappedInit = hasUnaryPrefix(init, '+', '-')
106 ? init.argument
107 : init;
108 return (isIdentifier(unwrappedInit, 'Infinity', 'NaN') ||
109 isFunctionCall(unwrappedInit, 'Number') ||
110 isLiteral(unwrappedInit, 'number'));
111 }
112 case utils_1.AST_NODE_TYPES.TSNullKeyword:
113 return init.type === utils_1.AST_NODE_TYPES.Literal && init.value === null;
114 case utils_1.AST_NODE_TYPES.TSStringKeyword:
115 return (isFunctionCall(init, 'String') ||
116 isLiteral(init, 'string') ||
117 init.type === utils_1.AST_NODE_TYPES.TemplateLiteral);
118 case utils_1.AST_NODE_TYPES.TSSymbolKeyword:
119 return isFunctionCall(init, 'Symbol');
120 case utils_1.AST_NODE_TYPES.TSTypeReference: {
121 if (annotation.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
122 annotation.typeName.name === 'RegExp') {
123 const isRegExpLiteral = init.type === utils_1.AST_NODE_TYPES.Literal &&
124 init.value instanceof RegExp;
125 const isRegExpNewCall = init.type === utils_1.AST_NODE_TYPES.NewExpression &&
126 init.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
127 init.callee.name === 'RegExp';
128 const isRegExpCall = isFunctionCall(init, 'RegExp');
129 return isRegExpLiteral || isRegExpCall || isRegExpNewCall;
130 }
131 return false;
132 }
133 case utils_1.AST_NODE_TYPES.TSUndefinedKeyword:
134 return (hasUnaryPrefix(init, 'void') || isIdentifier(init, 'undefined'));
135 }
136 return false;
137 }
138 /**
139 * Reports an inferrable type declaration, if any
140 */
141 function reportInferrableType(node, typeNode, initNode) {
142 if (!typeNode || !initNode || !typeNode.typeAnnotation) {
143 return;
144 }
145 if (!isInferrable(typeNode.typeAnnotation, initNode)) {
146 return;
147 }
148 const type = typeNode.typeAnnotation.type === utils_1.AST_NODE_TYPES.TSTypeReference
149 ? // TODO - if we add more references
150 'RegExp'
151 : keywordMap[typeNode.typeAnnotation.type];
152 context.report({
153 node,
154 messageId: 'noInferrableType',
155 data: {
156 type,
157 },
158 fix: fixer => fixer.remove(typeNode),
159 });
160 }
161 function inferrableVariableVisitor(node) {
162 if (!node.id) {
163 return;
164 }
165 reportInferrableType(node, node.id.typeAnnotation, node.init);
166 }
167 function inferrableParameterVisitor(node) {
168 if (ignoreParameters || !node.params) {
169 return;
170 }
171 node.params.filter(param => param.type === utils_1.AST_NODE_TYPES.AssignmentPattern &&
172 param.left &&
173 param.right).forEach(param => {
174 reportInferrableType(param, param.left.typeAnnotation, param.right);
175 });
176 }
177 function inferrablePropertyVisitor(node) {
178 // We ignore `readonly` because of Microsoft/TypeScript#14416
179 // Essentially a readonly property without a type
180 // will result in its value being the type, leading to
181 // compile errors if the type is stripped.
182 if (ignoreProperties || node.readonly || node.optional) {
183 return;
184 }
185 reportInferrableType(node, node.typeAnnotation, node.value);
186 }
187 return {
188 VariableDeclarator: inferrableVariableVisitor,
189 FunctionExpression: inferrableParameterVisitor,
190 FunctionDeclaration: inferrableParameterVisitor,
191 ArrowFunctionExpression: inferrableParameterVisitor,
192 PropertyDefinition: inferrablePropertyVisitor,
193 };
194 },
195});
196//# sourceMappingURL=no-inferrable-types.js.map
\No newline at end of file