Skip to content

Commit

Permalink
fix(eslint-plugin): [method-signature-style] handle multiline params (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
bradzacher committed Apr 7, 2020
1 parent 4b799b9 commit 5832a86
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 22 deletions.
42 changes: 33 additions & 9 deletions packages/eslint-plugin/src/rules/method-signature-style.ts
Expand Up @@ -4,11 +4,10 @@ import {
} from '@typescript-eslint/experimental-utils';
import * as util from '../util';

export type Options = ['property' | 'method'];
export type Options = [('property' | 'method')?];
export type MessageIds = 'errorMethod' | 'errorProperty';

export type MessageId = 'errorMethod' | 'errorProperty';

export default util.createRule<Options, MessageId>({
export default util.createRule<Options, MessageIds>({
name: 'method-signature-style',
meta: {
type: 'suggestion',
Expand Down Expand Up @@ -56,10 +55,21 @@ export default util.createRule<Options, MessageId>({
): string {
let params = '()';
if (node.params.length > 0) {
const openingParen = util.nullThrows(
sourceCode.getTokenBefore(node.params[0], util.isOpeningParenToken),
'Missing opening paren before first parameter',
);
const closingParen = util.nullThrows(
sourceCode.getTokenAfter(
node.params[node.params.length - 1],
util.isClosingParenToken,
),
'Missing closing paren after last parameter',
);

params = sourceCode.text.substring(
sourceCode.getTokenBefore(node.params[0])!.range[0],
sourceCode.getTokenAfter(node.params[node.params.length - 1])!
.range[1],
openingParen.range[0],
closingParen.range[1],
);
}
if (node.typeParameters != null) {
Expand All @@ -75,6 +85,18 @@ export default util.createRule<Options, MessageId>({
return sourceCode.getText(node.returnType!.typeAnnotation);
}

function getDelimiter(node: TSESTree.Node): string {
const lastToken = sourceCode.getLastToken(node);
if (
lastToken &&
(util.isSemicolonToken(lastToken) || util.isCommaToken(lastToken))
) {
return lastToken.value;
}

return '';
}

return {
TSMethodSignature(methodNode): void {
if (mode === 'method') {
Expand All @@ -88,9 +110,10 @@ export default util.createRule<Options, MessageId>({
const key = getMethodKey(methodNode);
const params = getMethodParams(methodNode);
const returnType = getMethodReturnType(methodNode);
const delimiter = getDelimiter(methodNode);
return fixer.replaceText(
methodNode,
`${key}: ${params} => ${returnType}`,
`${key}: ${params} => ${returnType}${delimiter}`,
);
},
});
Expand All @@ -112,9 +135,10 @@ export default util.createRule<Options, MessageId>({
const key = getMethodKey(propertyNode);
const params = getMethodParams(typeNode);
const returnType = getMethodReturnType(typeNode);
const delimiter = getDelimiter(propertyNode);
return fixer.replaceText(
propertyNode,
`${key}${params}: ${returnType}`,
`${key}${params}: ${returnType}${delimiter}`,
);
},
});
Expand Down
145 changes: 132 additions & 13 deletions packages/eslint-plugin/tests/rules/method-signature-style.test.ts
Expand Up @@ -7,19 +7,36 @@ const ruleTester = new RuleTester({

ruleTester.run('method-signature-style', rule, {
valid: [
...batchedSingleLineTests({
code: noFormat`
interface Test { f: (a: string) => number }
interface Test { ['f']: (a: boolean) => void }
interface Test { f: <T>(a: T) => T }
interface Test { ['f']: <T extends {}>(a: T, b: T) => T }
interface Test { 'f!': </* a */>(/* b */ x: any /* c */) => void }
type Test = { readonly f: (a: string) => number }
type Test = { ['f']?: (a: boolean) => void }
type Test = { readonly f?: <T>(a?: T) => T }
type Test = { readonly ['f']?: <T>(a: T, b: T) => T }
`,
}),
`
interface Test {
f: (a: string) => number;
}
`,
`
interface Test {
['f']: (a: boolean) => void;
}
`,
`
interface Test {
f: <T>(a: T) => T;
}
`,
`
interface Test {
['f']: <T extends {}>(a: T, b: T) => T;
}
`,
// TODO - requires prettier2 to format correctly
noFormat`
interface Test {
'f!': </* a */>(/* b */ x: any /* c */) => void;
}
`,
'type Test = { readonly f: (a: string) => number };',
"type Test = { ['f']?: (a: boolean) => void };",
'type Test = { readonly f?: <T>(a?: T) => T };',
"type Test = { readonly ['f']?: <T>(a: T, b: T) => T };",
...batchedSingleLineTests({
options: ['method'],
code: noFormat`
Expand Down Expand Up @@ -107,5 +124,107 @@ ruleTester.run('method-signature-style', rule, {
type Test = { readonly ['f']?<T>(a: T, b: T): T }
`,
}),
{
code: noFormat`
interface Foo {
semi(arg: string): void;
comma(arg: string): void,
none(arg: string): void
}
`,
output: noFormat`
interface Foo {
semi: (arg: string) => void;
comma: (arg: string) => void,
none: (arg: string) => void
}
`,
errors: [
{
messageId: 'errorMethod',
line: 3,
},
{
messageId: 'errorMethod',
line: 4,
},
{
messageId: 'errorMethod',
line: 5,
},
],
},
{
code: noFormat`
interface Foo {
semi: (arg: string) => void;
comma: (arg: string) => void,
none: (arg: string) => void
}
`,
output: noFormat`
interface Foo {
semi(arg: string): void;
comma(arg: string): void,
none(arg: string): void
}
`,
options: ['method'],
errors: [
{
messageId: 'errorProperty',
line: 3,
},
{
messageId: 'errorProperty',
line: 4,
},
{
messageId: 'errorProperty',
line: 5,
},
],
},
// https://github.com/typescript-eslint/typescript-eslint/issues/1857
{
code: noFormat`
interface Foo {
x(
args: Pick<
Bar,
'one' | 'two' | 'three'
>,
): Baz;
y(
foo: string,
bar: number,
): void;
}
`,
output: noFormat`
interface Foo {
x: (
args: Pick<
Bar,
'one' | 'two' | 'three'
>,
) => Baz;
y: (
foo: string,
bar: number,
) => void;
}
`,
errors: [
{
messageId: 'errorMethod',
line: 3,
},
{
messageId: 'errorMethod',
line: 9,
},
],
},
],
});

0 comments on commit 5832a86

Please sign in to comment.