Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat(eslint-plugin): more optional chain support in rules (#1363)
  • Loading branch information
bradzacher committed Dec 21, 2019
1 parent fd37bc3 commit 3dd1b02
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 4 deletions.
Expand Up @@ -25,8 +25,8 @@ export default util.createRule({
const checker = service.program.getTypeChecker();

return {
"CallExpression[arguments.length=0] > MemberExpression[property.name='sort'][computed=false]"(
node: TSESTree.MemberExpression,
":matches(CallExpression, OptionalCallExpression)[arguments.length=0] > :matches(MemberExpression, OptionalMemberExpression)[property.name='sort'][computed=false]"(
node: TSESTree.MemberExpression | TSESTree.OptionalMemberExpression,
): void {
// Get the symbol of the `sort` method.
const tsNode = service.esTreeNodeToTSNodeMap.get(node);
Expand Down
6 changes: 5 additions & 1 deletion packages/eslint-plugin/src/rules/unbound-method.ts
Expand Up @@ -55,7 +55,9 @@ export default util.createRule<Options, MessageIds>({
const checker = parserServices.program.getTypeChecker();

return {
MemberExpression(node): void {
'MemberExpression, OptionalMemberExpression'(
node: TSESTree.MemberExpression | TSESTree.OptionalMemberExpression,
): void {
if (isSafeUse(node)) {
return;
}
Expand Down Expand Up @@ -103,12 +105,14 @@ function isSafeUse(node: TSESTree.Node): boolean {
case AST_NODE_TYPES.IfStatement:
case AST_NODE_TYPES.ForStatement:
case AST_NODE_TYPES.MemberExpression:
case AST_NODE_TYPES.OptionalMemberExpression:
case AST_NODE_TYPES.SwitchStatement:
case AST_NODE_TYPES.UpdateExpression:
case AST_NODE_TYPES.WhileStatement:
return true;

case AST_NODE_TYPES.CallExpression:
case AST_NODE_TYPES.OptionalCallExpression:
return parent.callee === node;

case AST_NODE_TYPES.ConditionalExpression:
Expand Down
Expand Up @@ -72,6 +72,22 @@ ruleTester.run('require-array-sort-compare', rule, {
}
}
`,
// optional chain
`
function f(a: any[]) {
a?.sort((a, b) => a - b)
}
`,
`
namespace UserDefined {
interface Array {
sort(): void
}
function f(a: Array) {
a?.sort()
}
}
`,
],
invalid: [
{
Expand Down Expand Up @@ -123,5 +139,14 @@ ruleTester.run('require-array-sort-compare', rule, {
`,
errors: [{ messageId: 'requireCompare' }],
},
// optional chain
{
code: `
function f(a: string[]) {
a?.sort()
}
`,
errors: [{ messageId: 'requireCompare' }],
},
],
});
56 changes: 55 additions & 1 deletion packages/eslint-plugin/tests/rules/unbound-method.test.ts
Expand Up @@ -116,7 +116,8 @@ typeof instance.unbound === 'function';
typeof ContainsMethods.boundStatic === 'function';
typeof ContainsMethods.unboundStatic === 'function';
`,
`interface RecordA {
`
interface RecordA {
readonly type: "A"
readonly a: {}
}
Expand All @@ -143,6 +144,29 @@ class CommunicationError {
class CommunicationError {}
const x = CommunicationError.prototype;
`,
// optional chain
`
class ContainsMethods {
bound?: () => void;
unbound?(): void;
static boundStatic?: () => void;
static unboundStatic?(): void;
}
function foo(instance: ContainsMethods | null) {
instance?.bound();
instance?.unbound();
instance?.bound++;
if (instance?.bound) { }
if (instance?.unbound) { }
typeof instance?.bound === 'function';
typeof instance?.unbound === 'function';
}
`,
],
invalid: [
{
Expand All @@ -154,6 +178,36 @@ class ContainsMethods {
static unboundStatic?(): void;
}
function foo(instance: ContainsMethods | null) {
const unbound = instance?.unbound;
instance.unbound += 1;
instance?.unbound as any;
}
`,
errors: [
{
line: 10,
messageId: 'unbound',
},
{
line: 11,
messageId: 'unbound',
},
{
line: 12,
messageId: 'unbound',
},
],
},
{
code: `
class ContainsMethods {
bound?: () => void;
unbound?(): void;
static boundStatic?: () => void;
static unboundStatic?(): void;
}
const instance = new ContainsMethods();
{
Expand Down

0 comments on commit 3dd1b02

Please sign in to comment.