Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(typescript-estree): add strict type mapping to parserServices.esTreeNodeToTSNodeMap #1382

Merged
merged 11 commits into from Jan 9, 2020
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
6 changes: 3 additions & 3 deletions packages/eslint-plugin/src/rules/no-unnecessary-condition.ts
Expand Up @@ -121,7 +121,7 @@ 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);
}
Expand All @@ -130,7 +130,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 @@ -160,7 +160,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
25 changes: 16 additions & 9 deletions packages/eslint-plugin/src/rules/prefer-readonly.ts
Expand Up @@ -5,6 +5,7 @@ import { typeIsOrHasBaseType } from '../util';
import {
TSESTree,
AST_NODE_TYPES,
TSESTreeToTSNode,
} from '@typescript-eslint/experimental-utils';

type MessageIds = 'preferReadonly';
Expand All @@ -15,6 +16,8 @@ type Options = [
},
];

type ConstructorTSType = TSESTreeToTSNode<TSESTree.MethodDefinition>;

const functionScopeBoundaries = [
AST_NODE_TYPES.ArrowFunctionExpression,
AST_NODE_TYPES.FunctionDeclaration,
Expand Down Expand Up @@ -135,15 +138,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 +217,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 +237,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 +346,7 @@ class ClassScope {
).add(node.name.text);
}

public enterConstructor(node: ts.ConstructorDeclaration): void {
public enterConstructor(node: ConstructorTSType): 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/src/index.ts
Expand Up @@ -15,5 +15,6 @@ export {
AST_NODE_TYPES,
AST_TOKEN_TYPES,
TSESTree,
TSESTreeToTSNode,
bradzacher marked this conversation as resolved.
Show resolved Hide resolved
} from '@typescript-eslint/typescript-estree/dist/ts-estree';
export { ParserServices } from '@typescript-eslint/typescript-estree/dist/parser-options';