-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
fix(eslint-plugin): [prefer-function-type] handle this
type
#1783
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -5,6 +5,13 @@ import { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} from '@typescript-eslint/experimental-utils'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import * as util from '../util'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const possibleReturnThisTypeHolders = new Set([ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
AST_NODE_TYPES.TSTypeReference, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
AST_NODE_TYPES.TSThisType, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
AST_NODE_TYPES.TSFunctionType, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
AST_NODE_TYPES.TSTypeAnnotation, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
export default util.createRule({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
name: 'prefer-function-type', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
meta: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -26,6 +33,35 @@ export default util.createRule({ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
create(context) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const sourceCode = context.getSourceCode(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* Get the range of the `this` which is being used as a type annotation else returns null | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* @param node The node being checked | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* @returns {Array<number> | null} the range or null if no `this` type annotation are found | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
function getReturnType( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
node: TSESTree.TSTypeAnnotation, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
): Array<number> | null { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (!possibleReturnThisTypeHolders.has(node.type)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (node.type === AST_NODE_TYPES.TSThisType) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return node.range; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (node.type === AST_NODE_TYPES.TSTypeAnnotation) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (node.typeAnnotation.type === AST_NODE_TYPES.TSThisType) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return node.typeAnnotation.range; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (node.type === AST_NODE_TYPES.TSFunctionType) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return getReturnType(node?.returnType); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+41
to
+63
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried this function getReturnType(
node: TSESTree.TSTypeAnnotation,
): Array<number> | null {
if (!possibleReturnThisTypeHolders.has(node.type)) {
return null;
}
if (node.typeAnnotation.type === AST_NODE_TYPES.TSThisType) {
return node.range;
}
if (node.typeAnnotation.type === AST_NODE_TYPES.TSTypeAnnotation) { // <--- type error
if (node.typeAnnotation.type === AST_NODE_TYPES.TSThisType) { // <--- type error
return node.typeAnnotation.range;
}
}
if (node.typeAnnotation.type === AST_NODE_TYPES.TSFunctionType) {
return getReturnType(node?.returnType);
}
return null;
} Still no luck, even tests are failing cause of this ! |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* Checks if there the interface has exactly one supertype that isn't named 'Function' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* @param node The node being checked | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -76,11 +112,32 @@ export default util.createRule({ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const start = call.range[0]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const colonPos = call.returnType!.range[0] - start; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const text = sourceCode.getText().slice(start, call.range[1]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const returnType = getReturnType(call.returnType?.typeAnnotation); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let lhs = text.slice(0, colonPos); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let rhs = text.slice(colonPos + 1); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let suggestion = `${text.slice(0, colonPos)} =>${text.slice( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
colonPos + 1, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
)}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (returnType !== null && returnType.length === 2) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sourceCode.getText().slice(returnType[0], returnType[1]) === 'this' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// safe to change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
rhs = rhs.replace('this', parent.id.name); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (call.params.length > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
call.params.forEach(param => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
param.typeAnnotation?.typeAnnotation?.type === | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
AST_NODE_TYPES.TSThisType | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// replacing each first occurance of `this` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
lhs = lhs.replace('this', parent.id.name); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let suggestion = `${lhs} =>${rhs}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (shouldWrapSuggestion(parent.parent)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
suggestion = `(${suggestion})`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,6 +38,7 @@ interface Foo { | |
interface Bar extends Function, Foo { | ||
(): void; | ||
}`, | ||
'interface Interface {(arg: this): this, [key : number] : string}', | ||
], | ||
|
||
invalid: [ | ||
|
@@ -129,5 +130,65 @@ interface Foo<T> { | |
output: ` | ||
type Foo<T> = (bar: T) => string;`, | ||
}, | ||
{ | ||
code: 'interface Foo { (): () => this; }', | ||
errors: [ | ||
{ | ||
messageId: 'functionTypeOverCallableType', | ||
tyoe: AST_NODE_TYPES.TSCallSignatureDeclaration, | ||
}, | ||
], | ||
output: `type Foo = () => () => Foo;`, | ||
}, | ||
{ | ||
code: 'interface Foo { (): () => string; }', | ||
errors: [ | ||
{ | ||
messageId: 'functionTypeOverCallableType', | ||
tyoe: AST_NODE_TYPES.TSCallSignatureDeclaration, | ||
}, | ||
], | ||
output: `type Foo = () => () => string;`, | ||
}, | ||
{ | ||
code: 'interface Interface {(): this}', | ||
errors: [ | ||
{ | ||
messageId: 'functionTypeOverCallableType', | ||
tyoe: AST_NODE_TYPES.TSCallSignatureDeclaration, | ||
}, | ||
], | ||
output: `type Interface = () => Interface`, | ||
}, | ||
{ | ||
code: 'interface Interface {(): Object<this>}', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bradzacher this ( generic types ) will fail as the latest commit is still WIP. |
||
errors: [ | ||
{ | ||
messageId: 'functionTypeOverCallableType', | ||
tyoe: AST_NODE_TYPES.TSCallSignatureDeclaration, | ||
}, | ||
], | ||
output: `type Foo = () => () => this;`, | ||
anikethsaha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}, | ||
{ | ||
code: 'interface Interface {() : string}', | ||
errors: [ | ||
{ | ||
messageId: 'functionTypeOverCallableType', | ||
tyoe: AST_NODE_TYPES.TSCallSignatureDeclaration, | ||
}, | ||
], | ||
output: `type Interface = () => string`, | ||
}, | ||
{ | ||
code: 'interface Interface {(arg: this): this}', | ||
errors: [ | ||
{ | ||
messageId: 'functionTypeOverCallableType', | ||
tyoe: AST_NODE_TYPES.TSCallSignatureDeclaration, | ||
}, | ||
], | ||
output: `type Interface = (arg: Interface) => Interface`, | ||
}, | ||
], | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is failing the typecheck because it can never be true.
Your argument is of type
TSESTree.TypeAnnotation
, which is a container node.See my other comment for a suggestion of how to structure the function.