Skip to content

Commit

Permalink
feat(typescript-estree): add strict type mapping to esTreeNodeToTSNod…
Browse files Browse the repository at this point in the history
…eMap (#1382)
  • Loading branch information
armano2 authored and bradzacher committed Jan 9, 2020
1 parent a2cd3a7 commit d3d70a3
Show file tree
Hide file tree
Showing 25 changed files with 394 additions and 76 deletions.
4 changes: 1 addition & 3 deletions packages/eslint-plugin/src/rules/await-thenable.ts
Expand Up @@ -26,9 +26,7 @@ export default util.createRule({

return {
AwaitExpression(node): void {
const originalNode = parserServices.esTreeNodeToTSNodeMap.get<
ts.AwaitExpression
>(node);
const originalNode = parserServices.esTreeNodeToTSNodeMap.get(node);
const type = checker.getTypeAtLocation(originalNode.expression);

if (
Expand Down
4 changes: 1 addition & 3 deletions packages/eslint-plugin/src/rules/no-floating-promises.ts
Expand Up @@ -44,9 +44,7 @@ export default util.createRule<Options, 'floating'>({

return {
ExpressionStatement(node): void {
const { expression } = parserServices.esTreeNodeToTSNodeMap.get<
ts.ExpressionStatement
>(node);
const { expression } = parserServices.esTreeNodeToTSNodeMap.get(node);

if (isUnhandledPromise(checker, expression)) {
context.report({
Expand Down
4 changes: 1 addition & 3 deletions packages/eslint-plugin/src/rules/no-for-in-array.ts
Expand Up @@ -23,9 +23,7 @@ export default util.createRule({
ForInStatement(node): void {
const parserServices = util.getParserServices(context);
const checker = parserServices.program.getTypeChecker();
const originalNode = parserServices.esTreeNodeToTSNodeMap.get<
ts.ForInStatement
>(node);
const originalNode = parserServices.esTreeNodeToTSNodeMap.get(node);

const type = checker.getTypeAtLocation(originalNode.expression);

Expand Down
4 changes: 1 addition & 3 deletions packages/eslint-plugin/src/rules/no-misused-promises.ts
Expand Up @@ -99,9 +99,7 @@ export default util.createRule<Options, 'conditional' | 'voidReturn'>({
| TSESTree.OptionalCallExpression
| TSESTree.NewExpression,
): void {
const tsNode = parserServices.esTreeNodeToTSNodeMap.get<
ts.CallExpression | ts.NewExpression
>(node);
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
const voidParams = voidFunctionParams(checker, tsNode);
if (voidParams.size === 0) {
return;
Expand Down
8 changes: 4 additions & 4 deletions packages/eslint-plugin/src/rules/no-unnecessary-condition.ts
Expand Up @@ -136,12 +136,12 @@ export default createRule<Options, MessageId>({
const checker = service.program.getTypeChecker();
const sourceCode = context.getSourceCode();

function getNodeType(node: TSESTree.Node): ts.Type {
function getNodeType(node: TSESTree.Expression): ts.Type {
const tsNode = service.esTreeNodeToTSNodeMap.get(node);
return getConstrainedTypeAtLocation(checker, tsNode);
}

function nodeIsArrayType(node: TSESTree.Node): boolean {
function nodeIsArrayType(node: TSESTree.Expression): boolean {
const nodeType = getNodeType(node);
return checker.isArrayType(nodeType) || checker.isTupleType(nodeType);
}
Expand All @@ -150,7 +150,7 @@ export default createRule<Options, MessageId>({
* Checks if a conditional node is necessary:
* if the type of the node is always true or always false, it's not necessary.
*/
function checkNode(node: TSESTree.Node): void {
function checkNode(node: TSESTree.Expression): void {
const type = getNodeType(node);

// Conditional is always necessary if it involves:
Expand Down Expand Up @@ -180,7 +180,7 @@ export default createRule<Options, MessageId>({
}
}

function checkNodeForNullish(node: TSESTree.Node): void {
function checkNodeForNullish(node: TSESTree.Expression): void {
const type = getNodeType(node);
// Conditional is always necessary if it involves `any` or `unknown`
if (isTypeFlagSet(type, ts.TypeFlags.Any | ts.TypeFlags.Unknown)) {
Expand Down
15 changes: 11 additions & 4 deletions packages/eslint-plugin/src/rules/no-unnecessary-qualifier.ts
Expand Up @@ -74,7 +74,7 @@ export default util.createRule({
}

function qualifierIsUnnecessary(
qualifier: TSESTree.Node,
qualifier: TSESTree.EntityName | TSESTree.MemberExpression,
name: TSESTree.Identifier,
): boolean {
const tsQualifier = esTreeNodeToTSNodeMap.get(qualifier);
Expand Down Expand Up @@ -110,7 +110,7 @@ export default util.createRule({

function visitNamespaceAccess(
node: TSESTree.Node,
qualifier: TSESTree.Node,
qualifier: TSESTree.EntityName | TSESTree.MemberExpression,
name: TSESTree.Identifier,
): void {
// Only look for nested qualifier errors if we didn't already fail on the outer qualifier.
Expand All @@ -132,7 +132,12 @@ export default util.createRule({
}
}

function enterDeclaration(node: TSESTree.Node): void {
function enterDeclaration(
node:
| TSESTree.TSModuleDeclaration
| TSESTree.TSEnumDeclaration
| TSESTree.ExportNamedDeclaration,
): void {
namespacesInScope.push(esTreeNodeToTSNodeMap.get(node));
}

Expand All @@ -152,7 +157,9 @@ export default util.createRule({
return node.type === AST_NODE_TYPES.MemberExpression && !node.computed;
}

function isEntityNameExpression(node: TSESTree.Node): boolean {
function isEntityNameExpression(
node: TSESTree.Node,
): node is TSESTree.Identifier | TSESTree.MemberExpression {
return (
node.type === AST_NODE_TYPES.Identifier ||
(isPropertyAccessExpression(node) &&
Expand Down
Expand Up @@ -5,10 +5,14 @@ import * as util from '../util';
import { findFirstResult } from '../util';

type ParameterCapableTSNode =
| ts.TaggedTemplateExpression
| ts.ImportTypeNode
| ts.CallExpression
| ts.NewExpression
| ts.TypeReferenceNode
| ts.ExpressionWithTypeArguments;
| ts.ExpressionWithTypeArguments
| ts.JsxOpeningElement
| ts.JsxSelfClosingElement;

type MessageIds = 'unnecessaryTypeParameter';

Expand Down Expand Up @@ -67,9 +71,7 @@ export default util.createRule<[], MessageIds>({

return {
TSTypeParameterInstantiation(node): void {
const expression = parserServices.esTreeNodeToTSNodeMap.get<
ParameterCapableTSNode
>(node);
const expression = parserServices.esTreeNodeToTSNodeMap.get(node);

const typeParameters = getTypeParametersFromNode(expression, checker);
if (typeParameters) {
Expand Down
Expand Up @@ -168,9 +168,7 @@ export default util.createRule<Options, MessageIds>({

return {
TSNonNullExpression(node): void {
const originalNode = parserServices.esTreeNodeToTSNodeMap.get<
ts.NonNullExpression
>(node);
const originalNode = parserServices.esTreeNodeToTSNodeMap.get(node);
const type = util.getConstrainedTypeAtLocation(
checker,
originalNode.expression,
Expand Down Expand Up @@ -252,9 +250,7 @@ export default util.createRule<Options, MessageIds>({
return;
}

const originalNode = parserServices.esTreeNodeToTSNodeMap.get<
ts.AssertionExpression
>(node);
const originalNode = parserServices.esTreeNodeToTSNodeMap.get(node);
const castType = checker.getTypeAtLocation(originalNode);

if (
Expand Down
Expand Up @@ -290,7 +290,7 @@ export default util.createRule<Options, MessageIds>({
}

return {
'Program:exit'(program: TSESTree.Node): void {
'Program:exit'(program: TSESTree.Program): void {
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(program);
const sourceFile = util.getSourceFileOfNode(tsNode);
const diagnostics = tsProgram.getSemanticDiagnostics(sourceFile);
Expand Down
Expand Up @@ -4,7 +4,6 @@ import {
TSESLint,
TSESTree,
} from '@typescript-eslint/experimental-utils';
import * as ts from 'typescript';
import * as util from '../util';

export type Options = [
Expand Down Expand Up @@ -75,9 +74,7 @@ export default util.createRule<Options, MessageIds>({
'LogicalExpression[operator = "||"]'(
node: TSESTree.LogicalExpression,
): void {
const tsNode = parserServices.esTreeNodeToTSNodeMap.get<
ts.BinaryExpression
>(node);
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
const type = checker.getTypeAtLocation(tsNode.left);
const isNullish = util.isNullableType(type, { allowUndefined: true });
if (!isNullish) {
Expand Down
28 changes: 19 additions & 9 deletions packages/eslint-plugin/src/rules/prefer-readonly.ts
Expand Up @@ -135,15 +135,21 @@ export default util.createRule<Options, MessageIds>({
return false;
}

function isConstructor(node: TSESTree.Node): boolean {
function isConstructor(
node: TSESTree.Node,
): node is TSESTree.MethodDefinition {
return (
node.type === AST_NODE_TYPES.MethodDefinition &&
node.kind === 'constructor'
);
}

function isFunctionScopeBoundaryInStack(
node: TSESTree.Node,
node:
| TSESTree.ArrowFunctionExpression
| TSESTree.FunctionDeclaration
| TSESTree.FunctionExpression
| TSESTree.MethodDefinition,
): boolean | tsutils.ScopeBoundary {
if (classScopeStack.length === 0) {
return false;
Expand Down Expand Up @@ -208,10 +214,10 @@ export default util.createRule<Options, MessageIds>({
}
},
MemberExpression(node): void {
const tsNode = parserServices.esTreeNodeToTSNodeMap.get<
ts.PropertyAccessExpression
>(node);
if (classScopeStack.length !== 0 && !node.computed) {
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(
node,
) as ts.PropertyAccessExpression;
handlePropertyAccessExpression(
tsNode,
tsNode.parent,
Expand All @@ -228,9 +234,7 @@ export default util.createRule<Options, MessageIds>({
): void {
if (isConstructor(node)) {
classScopeStack[classScopeStack.length - 1].enterConstructor(
parserServices.esTreeNodeToTSNodeMap.get<ts.ConstructorDeclaration>(
node,
),
parserServices.esTreeNodeToTSNodeMap.get(node),
);
} else if (isFunctionScopeBoundaryInStack(node)) {
classScopeStack[classScopeStack.length - 1].enterNonConstructor();
Expand Down Expand Up @@ -339,7 +343,13 @@ class ClassScope {
).add(node.name.text);
}

public enterConstructor(node: ts.ConstructorDeclaration): void {
public enterConstructor(
node:
| ts.GetAccessorDeclaration
| ts.SetAccessorDeclaration
| ts.MethodDeclaration
| ts.ConstructorDeclaration,
): void {
this.constructorScopeDepth = DIRECTLY_INSIDE_CONSTRUCTOR;

for (const parameter of node.parameters) {
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-plugin/src/rules/prefer-regexp-exec.ts
Expand Up @@ -30,7 +30,7 @@ export default createRule({
* Check if a given node is a string.
* @param node The node to check.
*/
function isStringType(node: TSESTree.Node): boolean {
function isStringType(node: TSESTree.LeftHandSideExpression): boolean {
const objectType = typeChecker.getTypeAtLocation(
service.esTreeNodeToTSNodeMap.get(node),
);
Expand Down
Expand Up @@ -45,7 +45,7 @@ export default createRule({
* Check if a given node is a string.
* @param node The node to check.
*/
function isStringType(node: TSESTree.Node): boolean {
function isStringType(node: TSESTree.LeftHandSideExpression): boolean {
const objectType = typeChecker.getTypeAtLocation(
service.esTreeNodeToTSNodeMap.get(node),
);
Expand Down
9 changes: 8 additions & 1 deletion packages/eslint-plugin/src/rules/promise-function-async.ts
Expand Up @@ -90,7 +90,14 @@ export default util.createRule<Options, MessageIds>({
const parserServices = util.getParserServices(context);
const checker = parserServices.program.getTypeChecker();

function validateNode(node: TSESTree.Node): void {
function validateNode(
node:
| TSESTree.ArrowFunctionExpression
| TSESTree.FunctionDeclaration
| TSESTree.FunctionExpression
| TSESTree.MethodDefinition
| TSESTree.TSAbstractMethodDefinition,
): void {
const originalNode = parserServices.esTreeNodeToTSNodeMap.get(node);
const signatures = checker
.getTypeAtLocation(originalNode)
Expand Down
4 changes: 1 addition & 3 deletions packages/eslint-plugin/src/rules/require-await.ts
Expand Up @@ -66,9 +66,7 @@ export default util.createRule<Options, MessageIds>({
ForOfStatement: rules.ForOfStatement,

ReturnStatement(node): void {
const { expression } = parserServices.esTreeNodeToTSNodeMap.get<
ts.ReturnStatement
>(node);
const { expression } = parserServices.esTreeNodeToTSNodeMap.get(node);
if (expression && isThenableType(expression)) {
// tell the base rule to mark the scope as having an await so it ignores it
rules.AwaitExpression();
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-plugin/src/rules/restrict-plus-operands.ts
Expand Up @@ -96,7 +96,7 @@ export default util.createRule<Options, MessageIds>({
* Helper function to get base type of node
* @param node the node to be evaluated.
*/
function getNodeType(node: TSESTree.Node): BaseLiteral {
function getNodeType(node: TSESTree.Expression): BaseLiteral {
const tsNode = service.esTreeNodeToTSNodeMap.get(node);
const type = typeChecker.getTypeAtLocation(tsNode);

Expand Down
Expand Up @@ -92,7 +92,7 @@ export default util.createRule<Options, MessageId>({
* Helper function to get base type of node
* @param node the node to be evaluated.
*/
function getNodeType(node: TSESTree.Node): BaseType[] {
function getNodeType(node: TSESTree.Expression): BaseType[] {
const tsNode = service.esTreeNodeToTSNodeMap.get(node);
const type = typeChecker.getTypeAtLocation(tsNode);

Expand Down
4 changes: 1 addition & 3 deletions packages/eslint-plugin/src/rules/return-await.ts
Expand Up @@ -135,9 +135,7 @@ export default util.createRule({
}
},
ReturnStatement(node): void {
const originalNode = parserServices.esTreeNodeToTSNodeMap.get<
ts.ReturnStatement
>(node);
const originalNode = parserServices.esTreeNodeToTSNodeMap.get(node);

const { expression } = originalNode;

Expand Down
Expand Up @@ -66,10 +66,8 @@ export default util.createRule<Options, 'strictBooleanExpression'>({
/**
* Determines if the node is safe for boolean type
*/
function isValidBooleanNode(node: TSESTree.Node): boolean {
const tsNode = service.esTreeNodeToTSNodeMap.get<ts.ExpressionStatement>(
node,
);
function isValidBooleanNode(node: TSESTree.Expression): boolean {
const tsNode = service.esTreeNodeToTSNodeMap.get(node);
const type = util.getConstrainedTypeAtLocation(checker, tsNode);

if (tsutils.isTypeFlagSet(type, ts.TypeFlags.BooleanLike)) {
Expand Down
1 change: 1 addition & 0 deletions packages/experimental-utils/package.json
Expand Up @@ -26,6 +26,7 @@
},
"license": "MIT",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc -b tsconfig.build.json",
"clean": "tsc -b tsconfig.build.json --clean",
Expand Down

0 comments on commit d3d70a3

Please sign in to comment.