Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…ipt-eslint into feature/default-param-last
  • Loading branch information
a-tarasyuk committed Jan 10, 2020
2 parents e7d8a2f + 42b0fcc commit 36eb341
Show file tree
Hide file tree
Showing 34 changed files with 787 additions and 468 deletions.
2 changes: 1 addition & 1 deletion packages/eslint-plugin/ROADMAP.md
@@ -1,6 +1,6 @@
# TSLint Migration Guide

This document serves as a guid to help you migrate from TSLint.
This document serves as a guide to help you migrate from TSLint.
It lists all TSLint rules along side rules from the ESLint ecosystem that are the same or similar.

## TSLint rules
Expand Down
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
245 changes: 117 additions & 128 deletions packages/eslint-plugin/src/rules/no-magic-numbers.ts
Expand Up @@ -55,130 +55,6 @@ export default util.createRule<Options, MessageIds>({
create(context, [options]) {
const rules = baseRule.create(context);

/**
* Returns whether the node is number literal
* @param node the node literal being evaluated
* @returns true if the node is a number literal
*/
function isNumber(node: TSESTree.Literal): boolean {
return typeof node.value === 'number';
}

/**
* Checks if the node grandparent is a Typescript type alias declaration
* @param node the node to be validated.
* @returns true if the node grandparent is a Typescript type alias declaration
* @private
*/
function isGrandparentTSTypeAliasDeclaration(node: TSESTree.Node): boolean {
return node.parent && node.parent.parent
? node.parent.parent.type === AST_NODE_TYPES.TSTypeAliasDeclaration
: false;
}

/**
* Checks if the node grandparent is a Typescript union type and its parent is a type alias declaration
* @param node the node to be validated.
* @returns true if the node grandparent is a Typescript union type and its parent is a type alias declaration
* @private
*/
function isGrandparentTSUnionType(node: TSESTree.Node): boolean {
if (
node.parent &&
node.parent.parent &&
node.parent.parent.type === AST_NODE_TYPES.TSUnionType
) {
return isGrandparentTSTypeAliasDeclaration(node.parent);
}

return false;
}

/**
* Checks if the node parent is a Typescript enum member
* @param node the node to be validated.
* @returns true if the node parent is a Typescript enum member
* @private
*/
function isParentTSEnumDeclaration(node: TSESTree.Node): boolean {
return (
typeof node.parent !== 'undefined' &&
node.parent.type === AST_NODE_TYPES.TSEnumMember
);
}

/**
* Checks if the node parent is a Typescript literal type
* @param node the node to be validated.
* @returns true if the node parent is a Typescript literal type
* @private
*/
function isParentTSLiteralType(node: TSESTree.Node): boolean {
return node.parent
? node.parent.type === AST_NODE_TYPES.TSLiteralType
: false;
}

/**
* Checks if the node is a valid TypeScript numeric literal type.
* @param node the node to be validated.
* @returns true if the node is a TypeScript numeric literal type.
* @private
*/
function isTSNumericLiteralType(node: TSESTree.Node): boolean {
// For negative numbers, update the parent node
if (
node.parent &&
node.parent.type === AST_NODE_TYPES.UnaryExpression &&
node.parent.operator === '-'
) {
node = node.parent;
}

// If the parent node is not a TSLiteralType, early return
if (!isParentTSLiteralType(node)) {
return false;
}

// If the grandparent is a TSTypeAliasDeclaration, ignore
if (isGrandparentTSTypeAliasDeclaration(node)) {
return true;
}

// If the grandparent is a TSUnionType and it's parent is a TSTypeAliasDeclaration, ignore
if (isGrandparentTSUnionType(node)) {
return true;
}

return false;
}

/**
* Checks if the node parent is a readonly class property
* @param node the node to be validated.
* @returns true if the node parent is a readonly class property
* @private
*/
function isParentTSReadonlyClassProperty(node: TSESTree.Node): boolean {
if (
node.parent &&
node.parent.type === AST_NODE_TYPES.UnaryExpression &&
['-', '+'].includes(node.parent.operator)
) {
node = node.parent;
}

if (
node.parent &&
node.parent.type === AST_NODE_TYPES.ClassProperty &&
node.parent.readonly
) {
return true;
}

return false;
}

return {
Literal(node): void {
// Check if the node is a TypeScript enum declaration
Expand All @@ -189,14 +65,17 @@ export default util.createRule<Options, MessageIds>({
// Check TypeScript specific nodes for Numeric Literal
if (
options.ignoreNumericLiteralTypes &&
isNumber(node) &&
typeof node.value === 'number' &&
isTSNumericLiteralType(node)
) {
return;
}

// Check if the node is a readonly class property
if (isNumber(node) && isParentTSReadonlyClassProperty(node)) {
if (
typeof node.value === 'number' &&
isParentTSReadonlyClassProperty(node)
) {
if (options.ignoreReadonlyClassProperties) {
return;
}
Expand All @@ -207,8 +86,10 @@ export default util.createRule<Options, MessageIds>({
let raw = node.raw;

if (
node.parent &&
node.parent.type === AST_NODE_TYPES.UnaryExpression
node.parent?.type === AST_NODE_TYPES.UnaryExpression &&
// the base rule only shows the operator for negative numbers
// https://github.com/eslint/eslint/blob/9dfc8501fb1956c90dc11e6377b4cb38a6bea65d/lib/rules/no-magic-numbers.js#L126
node.parent.operator === '-'
) {
fullNumberNode = node.parent;
raw = `${node.parent.operator}${node.raw}`;
Expand All @@ -229,3 +110,111 @@ export default util.createRule<Options, MessageIds>({
};
},
});

/**
* Gets the true parent of the literal, handling prefixed numbers (-1 / +1)
*/
function getLiteralParent(node: TSESTree.Literal): TSESTree.Node | undefined {
if (
node.parent?.type === AST_NODE_TYPES.UnaryExpression &&
['-', '+'].includes(node.parent.operator)
) {
return node.parent.parent;
}

return node.parent;
}

/**
* Checks if the node grandparent is a Typescript type alias declaration
* @param node the node to be validated.
* @returns true if the node grandparent is a Typescript type alias declaration
* @private
*/
function isGrandparentTSTypeAliasDeclaration(node: TSESTree.Node): boolean {
return node.parent?.parent?.type === AST_NODE_TYPES.TSTypeAliasDeclaration;
}

/**
* Checks if the node grandparent is a Typescript union type and its parent is a type alias declaration
* @param node the node to be validated.
* @returns true if the node grandparent is a Typescript union type and its parent is a type alias declaration
* @private
*/
function isGrandparentTSUnionType(node: TSESTree.Node): boolean {
if (node.parent?.parent?.type === AST_NODE_TYPES.TSUnionType) {
return isGrandparentTSTypeAliasDeclaration(node.parent);
}

return false;
}

/**
* Checks if the node parent is a Typescript enum member
* @param node the node to be validated.
* @returns true if the node parent is a Typescript enum member
* @private
*/
function isParentTSEnumDeclaration(node: TSESTree.Literal): boolean {
const parent = getLiteralParent(node);
return parent?.type === AST_NODE_TYPES.TSEnumMember;
}

/**
* Checks if the node parent is a Typescript literal type
* @param node the node to be validated.
* @returns true if the node parent is a Typescript literal type
* @private
*/
function isParentTSLiteralType(node: TSESTree.Node): boolean {
return node.parent?.type === AST_NODE_TYPES.TSLiteralType;
}

/**
* Checks if the node is a valid TypeScript numeric literal type.
* @param node the node to be validated.
* @returns true if the node is a TypeScript numeric literal type.
* @private
*/
function isTSNumericLiteralType(node: TSESTree.Node): boolean {
// For negative numbers, use the parent node
if (
node.parent?.type === AST_NODE_TYPES.UnaryExpression &&
node.parent.operator === '-'
) {
node = node.parent;
}

// If the parent node is not a TSLiteralType, early return
if (!isParentTSLiteralType(node)) {
return false;
}

// If the grandparent is a TSTypeAliasDeclaration, ignore
if (isGrandparentTSTypeAliasDeclaration(node)) {
return true;
}

// If the grandparent is a TSUnionType and it's parent is a TSTypeAliasDeclaration, ignore
if (isGrandparentTSUnionType(node)) {
return true;
}

return false;
}

/**
* Checks if the node parent is a readonly class property
* @param node the node to be validated.
* @returns true if the node parent is a readonly class property
* @private
*/
function isParentTSReadonlyClassProperty(node: TSESTree.Literal): boolean {
const parent = getLiteralParent(node);

if (parent?.type === AST_NODE_TYPES.ClassProperty && parent.readonly) {
return true;
}

return false;
}
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

0 comments on commit 36eb341

Please sign in to comment.