Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

fix(eslint-plugin): [prefer-optional-chain] handle MemberExpression in final chain position#11835

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Open
mdm317 wants to merge3 commits intotypescript-eslint:main
base:main
Choose a base branch
Loading
frommdm317:fix-11822-prefer-optional-chain-memberexpression
Open
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -96,8 +96,7 @@ export default createRule<Options, MessageIds>({
const lhs = lhsName.typeAnnotation?.typeAnnotation;

if (
!rhs ||
rhs.type !== AST_NODE_TYPES.NewExpression ||
rhs?.type !== AST_NODE_TYPES.NewExpression ||
rhs.callee.type !== AST_NODE_TYPES.Identifier
) {
return;
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,10 +40,7 @@ export default createRule<Options, MessageIds>({
return;
}

if (
node.value &&
node.value.type === AST_NODE_TYPES.TSEmptyBodyFunctionExpression
) {
if (node.value?.type === AST_NODE_TYPES.TSEmptyBodyFunctionExpression) {
return;
}

Expand Down
5 changes: 1 addition & 4 deletionspackages/eslint-plugin/src/rules/no-misused-promises.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -657,10 +657,7 @@ export default createRule<Options, MessageId>({
}

function checkJSXAttribute(node: TSESTree.JSXAttribute): void {
if (
node.value == null ||
node.value.type !== AST_NODE_TYPES.JSXExpressionContainer
) {
if (node.value?.type !== AST_NODE_TYPES.JSXExpressionContainer) {
return;
}
const expressionContainer = services.esTreeNodeToTSNodeMap.get(
Expand Down
3 changes: 1 addition & 2 deletionspackages/eslint-plugin/src/rules/no-redeclare.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -271,9 +271,8 @@ export default createRule<Options, MessageIds>({
// Node.js or ES modules has a special scope.
if (
scope.type === ScopeType.global &&
scope.childScopes[0] &&
// The special scope's block is the Program node.
scope.block === scope.childScopes[0].block
scope.block === scope.childScopes[0]?.block
) {
findVariablesInScope(scope.childScopes[0]);
}
Expand Down
3 changes: 1 addition & 2 deletionspackages/eslint-plugin/src/rules/no-type-alias.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -229,8 +229,7 @@ export default createRule<Options, MessageIds>({
if (
type.node.type === AST_NODE_TYPES.TSTypeOperator &&
['keyof', 'readonly'].includes(type.node.operator) &&
type.node.typeAnnotation &&
type.node.typeAnnotation.type === AST_NODE_TYPES.TSTupleType
type.node.typeAnnotation?.type === AST_NODE_TYPES.TSTupleType
) {
return true;
}
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,8 +30,7 @@ export default createRule({
const moduleType = context.sourceCode.getTokenBefore(node.id);

if (
moduleType &&
moduleType.type === AST_TOKEN_TYPES.Identifier &&
moduleType?.type === AST_TOKEN_TYPES.Identifier &&
moduleType.value === 'module'
) {
context.report({
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -568,8 +568,7 @@ export default createRule<Options, MessageIds>({
}

if (
!assignmentExpression ||
assignmentExpression.type !== AST_NODE_TYPES.AssignmentExpression ||
assignmentExpression?.type !== AST_NODE_TYPES.AssignmentExpression ||
!isMemberAccessLike(assignmentExpression.left)
) {
return;
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,7 +31,15 @@ import {
} from '../../util';
import { checkNullishAndReport } from './checkNullishAndReport';
import { compareNodes, NodeComparisonResult } from './compareNodes';
import { ComparisonType, NullishComparisonType } from './gatherLogicalOperands';
import {
ComparisonType,
NullishComparisonType,
Yoda,
} from './gatherLogicalOperands';

type LastChainOperandForReport = Omit<LastChainOperand, 'yoda'> & {
isYoda: boolean;
};

function includesType(
parserServices: ParserServicesWithTypeInformation,
Expand DownExpand Up@@ -257,6 +265,57 @@ const analyzeOrChainOperand: OperandAnalyzer = (
}
};

const resolveOperandSubset = (
previousOperand: ValidOperand,
lastChainOperand: LastChainOperand,
) => {
const isNameSubset =
compareNodes(
previousOperand.comparedName,
lastChainOperand.comparedName,
) === NodeComparisonResult.Subset;

if (lastChainOperand.yoda !== Yoda.Unknown) {
return {
comparedName: lastChainOperand.comparedName,
comparisonValue: lastChainOperand.comparisonValue,
isSubset: isNameSubset,
isYoda: lastChainOperand.yoda === Yoda.Yes,
};
}

const isValueSubset =
compareNodes(
previousOperand.comparedName,
lastChainOperand.comparisonValue,
) === NodeComparisonResult.Subset;

if (isNameSubset && !isValueSubset) {
return {
comparedName: lastChainOperand.comparedName,
comparisonValue: lastChainOperand.comparisonValue,
isSubset: true,
isYoda: false,
};
}

if (!isNameSubset && isValueSubset) {
return {
comparedName: lastChainOperand.comparisonValue,
comparisonValue: lastChainOperand.comparedName,
isSubset: true,
isYoda: true,
};
}

return {
comparedName: lastChainOperand.comparisonValue,
comparisonValue: lastChainOperand.comparisonValue,
isSubset: false,
isYoda: true,
};
};

/**
* Returns the range that needs to be reported from the chain.
* @param chain The chain of logical expressions.
Expand DownExpand Up@@ -306,7 +365,7 @@ function getReportDescriptor(
operator: '&&' | '||',
options: PreferOptionalChainOptions,
subChain: ValidOperand[],
lastChain: (LastChainOperand | ValidOperand) | undefined,
lastChain: (LastChainOperandForReport | ValidOperand) | undefined,
): ReportDescriptor<PreferOptionalChainMessageIds> {
const chain = lastChain ? [...subChain, lastChain] : subChain;
const lastOperand = chain[chain.length - 1];
Expand DownExpand Up@@ -606,7 +665,9 @@ export function analyzeChain(
// Things like x !== null && x !== undefined have two nodes, but they are
// one logical unit here, so we'll allow them to be grouped.
let subChain: (readonly ValidOperand[] | ValidOperand)[] = [];
let lastChain: LastChainOperand | ValidOperand | undefined = undefined;
let lastChain: LastChainOperandForReport | ValidOperand | undefined =
undefined;

const maybeReportThenReset = (
newChainSeed?: readonly [ValidOperand, ...ValidOperand[]],
): void => {
Expand DownExpand Up@@ -710,23 +771,27 @@ export function analyzeChain(
const lastOperand = subChain.flat().at(-1);

if (lastOperand && lastChainOperand) {
const comparisonResult = compareNodes(
lastOperand.comparedName,
lastChainOperand.comparedName,
);
const isValidLastChainOperand =
operator === '&&'
? isValidAndLastChainOperand
: isValidOrLastChainOperand;

const { comparedName, comparisonValue, isSubset, isYoda } =
resolveOperandSubset(lastOperand, lastChainOperand);
if (
comparisonResult === NodeComparisonResult.Subset &&
isSubset &&
isValidLastChainOperand(
lastChainOperand.comparisonValue,
comparisonValue,
lastChainOperand.comparisonType,
parserServices,
)
) {
lastChain = lastChainOperand;
lastChain = {
...lastChainOperand,
comparedName,
comparisonValue,
isYoda,
};
}
}
// check the leftovers
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,6 +18,11 @@ import type { PreferOptionalChainOptions } from './PreferOptionalChainOptions';

import { isReferenceToGlobalFunction, isTypeFlagSet } from '../../util';

export const enum Yoda {
Yes,
No,
Unknown,
}
const enum ComparisonValueType {
Null = 'Null', // eslint-disable-line @typescript-eslint/internal/prefer-ast-types-enum
Undefined = 'Undefined',
Expand DownExpand Up@@ -66,7 +71,7 @@ export interface LastChainOperand {
comparedName: TSESTree.Node;
comparisonType: ComparisonType;
comparisonValue: TSESTree.Node;
isYoda: boolean;
yoda: Yoda;
node: TSESTree.BinaryExpression;
type: OperandValidity.Last;
}
Expand DownExpand Up@@ -257,7 +262,7 @@ export function gatherLogicalOperands(
// x !== something :(
const binaryComparisonChain = getBinaryComparisonChain(operand);
if (binaryComparisonChain) {
const { comparedName, comparedValue,isYoda } = binaryComparisonChain;
const { comparedName, comparedValue,yoda } = binaryComparisonChain;

switch (operand.operator) {
case '==':
Expand All@@ -270,9 +275,9 @@ export function gatherLogicalOperands(
comparedName,
comparisonType,
comparisonValue: comparedValue,
isYoda,
node: operand,
type: OperandValidity.Last,
yoda,
});
continue;
}
Expand All@@ -287,9 +292,9 @@ export function gatherLogicalOperands(
comparedName,
comparisonType,
comparisonValue: comparedValue,
isYoda,
node: operand,
type: OperandValidity.Last,
yoda,
});
continue;
}
Expand DownExpand Up@@ -430,29 +435,46 @@ export function gatherLogicalOperands(
return null;
}

function isMemberBasedExpression(
node: TSESTree.Expression | TSESTree.PrivateIdentifier,
): node is TSESTree.CallExpression | TSESTree.MemberExpression {
if (node.type === AST_NODE_TYPES.MemberExpression) {
return true;
}
if (
node.type === AST_NODE_TYPES.CallExpression &&
node.callee.type === AST_NODE_TYPES.MemberExpression
) {
return true;
}
return false;
}

function getBinaryComparisonChain(node: TSESTree.BinaryExpression) {
const { left, right } = node;
let isYoda = false;
const isLeftMemberExpression =
left.type === AST_NODE_TYPES.MemberExpression;
const isRightMemberExpression =
right.type === AST_NODE_TYPES.MemberExpression;
const isLeftMemberExpression = isMemberBasedExpression(left);
const isRightMemberExpression = isMemberBasedExpression(right);
if (isLeftMemberExpression && !isRightMemberExpression) {
const [comparedName, comparedValue] = [left, right];
return {
comparedName,
comparedValue,
isYoda,
yoda: Yoda.No,
};
}
if (!isLeftMemberExpression && isRightMemberExpression) {
const [comparedName, comparedValue] = [right, left];

isYoda = true;
return {
comparedName,
comparedValue,
isYoda,
yoda: Yoda.Yes,
};
}
if (isLeftMemberExpression && isRightMemberExpression) {
return {
comparedName: left,
comparedValue: right,
yoda: Yoda.Unknown,
};
}
return null;
Expand Down
13 changes: 3 additions & 10 deletionspackages/eslint-plugin/src/rules/unified-signatures.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -302,11 +302,7 @@ export default createRule<Options, MessageIds>({
}

function isThisParam(param: TSESTree.Parameter | undefined): boolean {
return (
param != null &&
param.type === AST_NODE_TYPES.Identifier &&
param.name === 'this'
);
return param?.type === AST_NODE_TYPES.Identifier && param.name === 'this';
}

function isThisVoidParam(param: TSESTree.Parameter | undefined) {
Expand DownExpand Up@@ -510,7 +506,7 @@ export default createRule<Options, MessageIds>({
a: TSESTree.TypeNode | undefined,
b: TSESTree.TypeNode | undefined,
): boolean {
return a === b || (a != null &&b != null &&a.type === b.type);
return a === b || (a != null && a.type === b?.type);
}

/* Returns the first index where `a` and `b` differ. */
Expand DownExpand Up@@ -595,10 +591,7 @@ export default createRule<Options, MessageIds>({
containingNode?: ContainingNode,
): void {
key ??= getOverloadKey(signature);
if (
currentScope &&
(containingNode ?? signature).parent === currentScope.parent
) {
if ((containingNode ?? signature).parent === currentScope?.parent) {
const overloads = currentScope.overloads.get(key);
if (overloads != null) {
overloads.push(signature);
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -315,7 +315,7 @@ abstract class ThisScope extends Visitor {
// ```
case AST_NODE_TYPES.VariableDeclarator: {
const value = firstDef.node.init;
if (value == null || value.type !== AST_NODE_TYPES.ThisExpression) {
if (value?.type !== AST_NODE_TYPES.ThisExpression) {
return null;
}

Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -125,7 +125,7 @@ function isConstructorArgument(
function isPropertyOfObjectWithType(
property: TSESTree.Node | undefined,
): boolean {
if (!property || property.type !== AST_NODE_TYPES.Property) {
if (property?.type !== AST_NODE_TYPES.Property) {
return false;
}
const objectExpr = property.parent;
Expand Down
5 changes: 1 addition & 4 deletionspackages/eslint-plugin/src/util/misc.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -66,10 +66,7 @@ export function arraysAreEqual<T>(
): boolean {
return (
a === b ||
(a != null &&
b != null &&
a.length === b.length &&
a.every((x, idx) => eq(x, b[idx])))
(a != null && a.length === b?.length && a.every((x, idx) => eq(x, b[idx])))
);
}

Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp