📄 no-inferrable-types¶
📊 Analysis Summary¶
| Metric | Count |
|---|---|
| 🔧 Functions | 10 |
| 📦 Imports | 6 |
| 📑 Type Aliases | 3 |
📚 Table of Contents¶
🛠️ File Location:¶
📂 packages/eslint-plugin/src/rules/no-inferrable-types.ts
📤 Default Export¶
| Property | Value |
|---|---|
name |
'no-inferrable-types' |
meta.type |
'suggestion' |
meta.docs.description |
'Disallow explicit type declarations for variables or parameters initialized to a number, string, or boolean' |
meta.docs.recommended |
'stylistic' |
meta.fixable |
'code' |
meta.messages.noInferrableType |
'Type {{type}} trivially inferred from a {{type}} literal, remove type annotation.' |
meta.schema |
[ { type: 'object', additionalProperties: false, properties: { ignoreParameters: { type: 'boolean', description: 'Whe... |
defaultOptions |
[ { ignoreParameters: false, ignoreProperties: false, }, ] |
Entry point: create — documented under Functions.
📦 Imports¶
| Name | Source |
|---|---|
TSESTree |
@typescript-eslint/utils |
AST_NODE_TYPES |
@typescript-eslint/utils |
createRule |
../util |
nullThrows |
../util |
NullThrowsReasons |
../util |
skipChainExpression |
../util |
Functions¶
create(context: any, [{ ignoreParameters, ignoreProp…: any): { AccessorProperty: (node: TSESTree.AccessorProperty | TSES…¶
Parameters:
contextany[{ ignoreParameters, ignoreProperties }]any
Returns: { AccessorProperty: (node: TSESTree.AccessorProperty | TSESTree.PropertyDefinition) => void; ArrowFunctionExpression: (node: TSESTree.ArrowFunctionExpression | TSESTree.FunctionDeclaration | TSESTree.FunctionExpression) => void; FunctionDeclaration: (node: TSESTree.ArrowFunctionExpression | TSESTree.FunctionDeclaration | TSESTree.FunctionExpression) => void; FunctionExpression: (node: TSESTree.ArrowFunctionExpression | TSESTree.FunctionDeclaration | TSESTree.FunctionExpression) => void; PropertyDefinition: (node: TSESTree.AccessorProperty | TSESTree.PropertyDefinition) => void; VariableDeclarator: (node: TSESTree.VariableDeclarator) => void; }
Calls:
skipChainExpression (from ../util)names.includesoperators.includeshasUnaryPrefixisFunctionCallisLiteralisIdentifierisInferrablecontext.reportfixer.removenullThrows (from ../util)context.sourceCode.getTokenBeforeNullThrowsReasons.MissingTokenreportInferrableTypenode.params.forEach
Internal Comments:
/**
* Returns whether a node has an inferrable value or not
*/
// note that bigint cannot have + prefixed to it (x2)
/**
* Reports an inferrable type declaration, if any
*/
// We ignore `readonly` because of Microsoft/TypeScript#14416
// Essentially a readonly property without a type
// will result in its value being the type, leading to
// compile errors if the type is stripped.
Code
create(context, [{ ignoreParameters, ignoreProperties }]) {
function isFunctionCall(
init: TSESTree.Expression,
callName: string,
): boolean {
const node = skipChainExpression(init);
return (
node.type === AST_NODE_TYPES.CallExpression &&
node.callee.type === AST_NODE_TYPES.Identifier &&
node.callee.name === callName
);
}
function isLiteral(init: TSESTree.Expression, typeName: string): boolean {
return (
init.type === AST_NODE_TYPES.Literal && typeof init.value === typeName
);
}
function isIdentifier(
init: TSESTree.Expression,
...names: string[]
): boolean {
return (
init.type === AST_NODE_TYPES.Identifier && names.includes(init.name)
);
}
function hasUnaryPrefix(
init: TSESTree.Expression,
...operators: string[]
): init is TSESTree.UnaryExpression {
return (
init.type === AST_NODE_TYPES.UnaryExpression &&
operators.includes(init.operator)
);
}
type Keywords =
| TSESTree.TSBigIntKeyword
| TSESTree.TSBooleanKeyword
| TSESTree.TSNullKeyword
| TSESTree.TSNumberKeyword
| TSESTree.TSStringKeyword
| TSESTree.TSSymbolKeyword
| TSESTree.TSTypeReference
| TSESTree.TSUndefinedKeyword;
const keywordMap = {
[AST_NODE_TYPES.TSBigIntKeyword]: 'bigint',
[AST_NODE_TYPES.TSBooleanKeyword]: 'boolean',
[AST_NODE_TYPES.TSNullKeyword]: 'null',
[AST_NODE_TYPES.TSNumberKeyword]: 'number',
[AST_NODE_TYPES.TSStringKeyword]: 'string',
[AST_NODE_TYPES.TSSymbolKeyword]: 'symbol',
[AST_NODE_TYPES.TSUndefinedKeyword]: 'undefined',
};
/**
* Returns whether a node has an inferrable value or not
*/
function isInferrable(
annotation: TSESTree.TypeNode,
init: TSESTree.Expression,
): annotation is Keywords {
switch (annotation.type) {
case AST_NODE_TYPES.TSBigIntKeyword: {
// note that bigint cannot have + prefixed to it
const unwrappedInit = hasUnaryPrefix(init, '-')
? init.argument
: init;
return (
isFunctionCall(unwrappedInit, 'BigInt') ||
unwrappedInit.type === AST_NODE_TYPES.Literal
);
}
case AST_NODE_TYPES.TSBooleanKeyword:
return (
hasUnaryPrefix(init, '!') ||
isFunctionCall(init, 'Boolean') ||
isLiteral(init, 'boolean')
);
case AST_NODE_TYPES.TSNumberKeyword: {
const unwrappedInit = hasUnaryPrefix(init, '+', '-')
? init.argument
: init;
return (
isIdentifier(unwrappedInit, 'Infinity', 'NaN') ||
isFunctionCall(unwrappedInit, 'Number') ||
isLiteral(unwrappedInit, 'number')
);
}
case AST_NODE_TYPES.TSNullKeyword:
return init.type === AST_NODE_TYPES.Literal && init.value == null;
case AST_NODE_TYPES.TSStringKeyword:
return (
isFunctionCall(init, 'String') ||
isLiteral(init, 'string') ||
init.type === AST_NODE_TYPES.TemplateLiteral
);
case AST_NODE_TYPES.TSSymbolKeyword:
return isFunctionCall(init, 'Symbol');
case AST_NODE_TYPES.TSTypeReference: {
if (
annotation.typeName.type === AST_NODE_TYPES.Identifier &&
annotation.typeName.name === 'RegExp'
) {
const isRegExpLiteral =
init.type === AST_NODE_TYPES.Literal &&
init.value instanceof RegExp;
const isRegExpNewCall =
init.type === AST_NODE_TYPES.NewExpression &&
init.callee.type === AST_NODE_TYPES.Identifier &&
init.callee.name === 'RegExp';
const isRegExpCall = isFunctionCall(init, 'RegExp');
return isRegExpLiteral || isRegExpCall || isRegExpNewCall;
}
return false;
}
case AST_NODE_TYPES.TSUndefinedKeyword:
return (
hasUnaryPrefix(init, 'void') || isIdentifier(init, 'undefined')
);
}
return false;
}
/**
* Reports an inferrable type declaration, if any
*/
function reportInferrableType(
node:
| TSESTree.AccessorProperty
| TSESTree.Parameter
| TSESTree.PropertyDefinition
| TSESTree.VariableDeclarator,
typeNode: TSESTree.TSTypeAnnotation | undefined,
initNode: TSESTree.Expression | null | undefined,
): void {
if (!typeNode || !initNode) {
return;
}
if (!isInferrable(typeNode.typeAnnotation, initNode)) {
return;
}
const type =
typeNode.typeAnnotation.type === AST_NODE_TYPES.TSTypeReference
? // TODO - if we add more references
'RegExp'
: keywordMap[typeNode.typeAnnotation.type];
context.report({
node,
messageId: 'noInferrableType',
data: {
type,
},
*fix(fixer) {
if (
(node.type === AST_NODE_TYPES.AssignmentPattern &&
node.left.optional) ||
(node.type === AST_NODE_TYPES.PropertyDefinition && node.definite)
) {
yield fixer.remove(
nullThrows(
context.sourceCode.getTokenBefore(typeNode),
NullThrowsReasons.MissingToken('token before', 'type node'),
),
);
}
yield fixer.remove(typeNode);
},
});
}
function inferrableVariableVisitor(
node: TSESTree.VariableDeclarator,
): void {
reportInferrableType(node, node.id.typeAnnotation, node.init);
}
function inferrableParameterVisitor(
node:
| TSESTree.ArrowFunctionExpression
| TSESTree.FunctionDeclaration
| TSESTree.FunctionExpression,
): void {
if (ignoreParameters) {
return;
}
node.params.forEach(param => {
if (param.type === AST_NODE_TYPES.TSParameterProperty) {
param = param.parameter;
}
if (param.type === AST_NODE_TYPES.AssignmentPattern) {
reportInferrableType(param, param.left.typeAnnotation, param.right);
}
});
}
function inferrablePropertyVisitor(
node: TSESTree.AccessorProperty | TSESTree.PropertyDefinition,
): void {
// We ignore `readonly` because of Microsoft/TypeScript#14416
// Essentially a readonly property without a type
// will result in its value being the type, leading to
// compile errors if the type is stripped.
if (ignoreProperties || node.readonly || node.optional) {
return;
}
reportInferrableType(node, node.typeAnnotation, node.value);
}
return {
AccessorProperty: inferrablePropertyVisitor,
ArrowFunctionExpression: inferrableParameterVisitor,
FunctionDeclaration: inferrableParameterVisitor,
FunctionExpression: inferrableParameterVisitor,
PropertyDefinition: inferrablePropertyVisitor,
VariableDeclarator: inferrableVariableVisitor,
};
}
Internal helpers¶
Declared inside another function in this file.
isFunctionCall(init: TSESTree.Expression, callName: string): boolean¶
Parameters:
initTSESTree.ExpressioncallNamestring
Returns: boolean
Calls:
skipChainExpression (from ../util)
Code
isLiteral(init: TSESTree.Expression, typeName: string): boolean¶
Parameters:
initTSESTree.ExpressiontypeNamestring
Returns: boolean
Code
isIdentifier(init: TSESTree.Expression, names: string[]): boolean¶
Parameters:
initTSESTree.Expressionnamesstring[]
Returns: boolean
Calls:
names.includes
Code
hasUnaryPrefix(init: TSESTree.Expression, operators: string[]): init is TSESTree.UnaryExpression¶
Parameters:
initTSESTree.Expressionoperatorsstring[]
Returns: init is TSESTree.UnaryExpression
Calls:
operators.includes
Code
isInferrable(annotation: TSESTree.TypeNode, init: TSESTree.Expression): annotation is Keywords¶
Returns whether a node has an inferrable value or not
Calls:
hasUnaryPrefixisFunctionCallisLiteralisIdentifier
Internal Comments:
Code
function isInferrable(
annotation: TSESTree.TypeNode,
init: TSESTree.Expression,
): annotation is Keywords {
switch (annotation.type) {
case AST_NODE_TYPES.TSBigIntKeyword: {
// note that bigint cannot have + prefixed to it
const unwrappedInit = hasUnaryPrefix(init, '-')
? init.argument
: init;
return (
isFunctionCall(unwrappedInit, 'BigInt') ||
unwrappedInit.type === AST_NODE_TYPES.Literal
);
}
case AST_NODE_TYPES.TSBooleanKeyword:
return (
hasUnaryPrefix(init, '!') ||
isFunctionCall(init, 'Boolean') ||
isLiteral(init, 'boolean')
);
case AST_NODE_TYPES.TSNumberKeyword: {
const unwrappedInit = hasUnaryPrefix(init, '+', '-')
? init.argument
: init;
return (
isIdentifier(unwrappedInit, 'Infinity', 'NaN') ||
isFunctionCall(unwrappedInit, 'Number') ||
isLiteral(unwrappedInit, 'number')
);
}
case AST_NODE_TYPES.TSNullKeyword:
return init.type === AST_NODE_TYPES.Literal && init.value == null;
case AST_NODE_TYPES.TSStringKeyword:
return (
isFunctionCall(init, 'String') ||
isLiteral(init, 'string') ||
init.type === AST_NODE_TYPES.TemplateLiteral
);
case AST_NODE_TYPES.TSSymbolKeyword:
return isFunctionCall(init, 'Symbol');
case AST_NODE_TYPES.TSTypeReference: {
if (
annotation.typeName.type === AST_NODE_TYPES.Identifier &&
annotation.typeName.name === 'RegExp'
) {
const isRegExpLiteral =
init.type === AST_NODE_TYPES.Literal &&
init.value instanceof RegExp;
const isRegExpNewCall =
init.type === AST_NODE_TYPES.NewExpression &&
init.callee.type === AST_NODE_TYPES.Identifier &&
init.callee.name === 'RegExp';
const isRegExpCall = isFunctionCall(init, 'RegExp');
return isRegExpLiteral || isRegExpCall || isRegExpNewCall;
}
return false;
}
case AST_NODE_TYPES.TSUndefinedKeyword:
return (
hasUnaryPrefix(init, 'void') || isIdentifier(init, 'undefined')
);
}
return false;
}
reportInferrableType(node: | TSESTree.AccessorProperty | TSESTree.…, typeNode: TSESTree.TSTypeAnnotation | undefined, initNode: TSESTree.Expression | null | undefined): void¶
Reports an inferrable type declaration, if any
Calls:
isInferrablecontext.reportfixer.removenullThrows (from ../util)context.sourceCode.getTokenBeforeNullThrowsReasons.MissingToken
Code
function reportInferrableType(
node:
| TSESTree.AccessorProperty
| TSESTree.Parameter
| TSESTree.PropertyDefinition
| TSESTree.VariableDeclarator,
typeNode: TSESTree.TSTypeAnnotation | undefined,
initNode: TSESTree.Expression | null | undefined,
): void {
if (!typeNode || !initNode) {
return;
}
if (!isInferrable(typeNode.typeAnnotation, initNode)) {
return;
}
const type =
typeNode.typeAnnotation.type === AST_NODE_TYPES.TSTypeReference
? // TODO - if we add more references
'RegExp'
: keywordMap[typeNode.typeAnnotation.type];
context.report({
node,
messageId: 'noInferrableType',
data: {
type,
},
*fix(fixer) {
if (
(node.type === AST_NODE_TYPES.AssignmentPattern &&
node.left.optional) ||
(node.type === AST_NODE_TYPES.PropertyDefinition && node.definite)
) {
yield fixer.remove(
nullThrows(
context.sourceCode.getTokenBefore(typeNode),
NullThrowsReasons.MissingToken('token before', 'type node'),
),
);
}
yield fixer.remove(typeNode);
},
});
}
inferrableVariableVisitor(node: TSESTree.VariableDeclarator): void¶
Parameters:
nodeTSESTree.VariableDeclarator
Returns: void
Calls:
reportInferrableType
Code
inferrableParameterVisitor(node: | TSESTree.ArrowFunctionExpression | TS…): void¶
Parameters:
node| TSESTree.ArrowFunctionExpression | TSESTree.FunctionDeclaration | TSESTree.FunctionExpression
Returns: void
Calls:
node.params.forEachreportInferrableType
Code
function inferrableParameterVisitor(
node:
| TSESTree.ArrowFunctionExpression
| TSESTree.FunctionDeclaration
| TSESTree.FunctionExpression,
): void {
if (ignoreParameters) {
return;
}
node.params.forEach(param => {
if (param.type === AST_NODE_TYPES.TSParameterProperty) {
param = param.parameter;
}
if (param.type === AST_NODE_TYPES.AssignmentPattern) {
reportInferrableType(param, param.left.typeAnnotation, param.right);
}
});
}
inferrablePropertyVisitor(node: TSESTree.AccessorProperty | TSESTree.Pr…): void¶
Parameters:
nodeTSESTree.AccessorProperty | TSESTree.PropertyDefinition
Returns: void
Calls:
reportInferrableType
Internal Comments:
// We ignore `readonly` because of Microsoft/TypeScript#14416
// Essentially a readonly property without a type
// will result in its value being the type, leading to
// compile errors if the type is stripped.
Code
function inferrablePropertyVisitor(
node: TSESTree.AccessorProperty | TSESTree.PropertyDefinition,
): void {
// We ignore `readonly` because of Microsoft/TypeScript#14416
// Essentially a readonly property without a type
// will result in its value being the type, leading to
// compile errors if the type is stripped.
if (ignoreProperties || node.readonly || node.optional) {
return;
}
reportInferrableType(node, node.typeAnnotation, node.value);
}
Type Aliases¶
Options¶
MessageIds¶
Keywords¶
type Keywords = | TSESTree.TSBigIntKeyword
| TSESTree.TSBooleanKeyword
| TSESTree.TSNullKeyword
| TSESTree.TSNumberKeyword
| TSESTree.TSStringKeyword
| TSESTree.TSSymbolKeyword
| TSESTree.TSTypeReference
| TSESTree.TSUndefinedKeyword;
Generated by Syntax Scribe