-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(eslint-plugin): added new rule no-untyped-public-signature (#801)
Co-authored-by: Brad Zacher <[email protected]>
- Loading branch information
1 parent
db1aa18
commit c5835f3
Showing
6 changed files
with
391 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
57 changes: 57 additions & 0 deletions
57
packages/eslint-plugin/docs/rules/no-untyped-public-signature.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# Disallow untyped public methods (no-untyped-public-signature) | ||
|
||
public methods are meant to be used by code outside of your class. By typing both the parameters and the return type of public methods they will be more readable and easy to use. | ||
|
||
## Rule Details | ||
|
||
This rule aims to ensure that only typed public methods are declared in the code. | ||
|
||
The following patterns are considered warnings: | ||
|
||
```ts | ||
// untyped parameter | ||
public foo(param1): void { | ||
} | ||
|
||
// untyped parameter | ||
public foo(param1: any): void { | ||
} | ||
|
||
// untyped return type | ||
public foo(param1: string) { | ||
} | ||
|
||
// untyped return type | ||
public foo(param1: string): any { | ||
} | ||
``` | ||
|
||
The following patterns are not warnings: | ||
|
||
```ts | ||
// typed public method | ||
public foo(param1: string): void { | ||
} | ||
|
||
// untyped private method | ||
private foo(param1) { | ||
} | ||
``` | ||
|
||
## Options | ||
|
||
This rule, in its default state, does not require any argument. | ||
|
||
### ignoredMethods | ||
|
||
You may pass method names you would like this rule to ignore, like so: | ||
|
||
```cjson | ||
{ | ||
"@typescript-eslint/no-untyped-public-signature": ["error", { "ignoredMethods": ["ignoredMethodName"] }] | ||
} | ||
``` | ||
|
||
## When Not To Use It | ||
|
||
If you don't wish to type public methods. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
120 changes: 120 additions & 0 deletions
120
packages/eslint-plugin/src/rules/no-untyped-public-signature.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
import * as util from '../util'; | ||
import { | ||
AST_NODE_TYPES, | ||
TSESTree, | ||
} from '@typescript-eslint/experimental-utils'; | ||
|
||
type MessageIds = 'noReturnType' | 'untypedParameter'; | ||
|
||
type Options = [{ ignoredMethods: string[] }]; | ||
|
||
export default util.createRule<Options, MessageIds>({ | ||
name: 'no-unused-public-signature', | ||
meta: { | ||
docs: { | ||
description: | ||
'Requires that all public method arguments and return type will be explicitly typed', | ||
category: 'Best Practices', | ||
recommended: false, | ||
}, | ||
messages: { | ||
noReturnType: 'Public method has no return type', | ||
untypedParameter: 'Public method parameters should be typed', | ||
}, | ||
schema: [ | ||
{ | ||
allowAdditionalProperties: false, | ||
properties: { | ||
ignoredMethods: { | ||
type: 'array', | ||
items: { | ||
type: 'string', | ||
}, | ||
}, | ||
}, | ||
type: 'object', | ||
}, | ||
], | ||
type: 'suggestion', | ||
}, | ||
defaultOptions: [{ ignoredMethods: [] }], | ||
create(context, [options]) { | ||
const ignoredMethods = new Set(options.ignoredMethods); | ||
|
||
function isPublicMethod( | ||
node: TSESTree.MethodDefinition | TSESTree.TSAbstractMethodDefinition, | ||
): boolean { | ||
return node.accessibility === 'public' || !node.accessibility; | ||
} | ||
|
||
function isIgnoredMethod( | ||
node: TSESTree.MethodDefinition | TSESTree.TSAbstractMethodDefinition, | ||
ignoredMethods: Set<string>, | ||
): boolean { | ||
if ( | ||
node.key.type === AST_NODE_TYPES.Literal && | ||
typeof node.key.value === 'string' | ||
) { | ||
return ignoredMethods.has(node.key.value); | ||
} | ||
if ( | ||
node.key.type === AST_NODE_TYPES.TemplateLiteral && | ||
node.key.expressions.length === 0 | ||
) { | ||
return ignoredMethods.has(node.key.quasis[0].value.raw); | ||
} | ||
if (node.key.type === AST_NODE_TYPES.Identifier && !node.computed) { | ||
return ignoredMethods.has(node.key.name); | ||
} | ||
|
||
return false; | ||
} | ||
|
||
function isParamTyped(node: TSESTree.Identifier): boolean { | ||
return ( | ||
!!node.typeAnnotation && | ||
node.typeAnnotation.typeAnnotation.type !== AST_NODE_TYPES.TSAnyKeyword | ||
); | ||
} | ||
|
||
function isReturnTyped( | ||
node: TSESTree.TSTypeAnnotation | undefined, | ||
): boolean { | ||
if (!node) { | ||
return false; | ||
} | ||
return ( | ||
node.typeAnnotation && | ||
node.typeAnnotation.type !== AST_NODE_TYPES.TSAnyKeyword | ||
); | ||
} | ||
|
||
return { | ||
'TSAbstractMethodDefinition, MethodDefinition'( | ||
node: TSESTree.MethodDefinition | TSESTree.TSAbstractMethodDefinition, | ||
): void { | ||
if (isPublicMethod(node) && !isIgnoredMethod(node, ignoredMethods)) { | ||
const paramIdentifiers = node.value.params.filter( | ||
param => param.type === AST_NODE_TYPES.Identifier, | ||
) as TSESTree.Identifier[]; | ||
const identifiersHaveTypes = paramIdentifiers.every(isParamTyped); | ||
if (!identifiersHaveTypes) { | ||
context.report({ | ||
node, | ||
messageId: 'untypedParameter', | ||
data: {}, | ||
}); | ||
} | ||
|
||
if (!isReturnTyped(node.value.returnType)) { | ||
context.report({ | ||
node, | ||
messageId: 'noReturnType', | ||
data: {}, | ||
}); | ||
} | ||
} | ||
}, | ||
}; | ||
}, | ||
}); |
Oops, something went wrong.