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(eslint-plugin): more optional chain support in rules #1363

Merged
merged 2 commits into from Dec 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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