-
-
Notifications
You must be signed in to change notification settings - Fork 74
Discussion: Extending core ESLint rules to "just work" with TS-specific nodes #77
Description
In agreement with @nzakas, I wanted to start a discussion here which summarises the findings so far on which core ESLint rules currently have issues with TypeScript-specific concepts, such as interfaces and decorators.
We do have a few options open to us with regards to making this all work (and the eslint-plugin-typescript
plugin will still have its place regardless), but we are in agreement that it would be awesome if we did not have to duplicate any existing rules where all we are trying to do is match the same functionality as on standard JS nodes.
I have been running all of the core rules over a large TypeScript codebase (definitely not an exhaustive use of TypeScript features in there, but it's a great start) and noted the following, some of which is fairly subjective:
Rule: camelcase
We have the ability to control whether or not camelcase
will apply to object properties, e.g.
"camelcase": ["error", { "properties": "never" }]
Opinion: We should be able to do this for TypeScript interfaces properties too
Rule: keyword-spacing
We can enforce spaces before and after keywords like so:
"keyword-spacing": ["error", { "before": true, "after": true }]
Opinion: We should be able to make type "casting" exempt from this in some way
E.g. I had this example:
<models.ICreativeTemplate>this.currentCreative.template
...where I would not want to put a space between the >
and this
keyword, but the rule naturally wants me to.
Rule: no-undef
With TypeScript, we are supporting class properties and interfaces. These are both causing false negatives with no-undef
:
class A {
foo = true
}
/* ESLint:
error, 'foo' is not defined
interface MyInterface {
bar: string,
baz: number
}
/* ESLint:
error, 'MyInterface' is not defined
error, 'bar' is not defined
error, 'baz' is not defined
It is very likely there are more TypeScript features that will cause the same, but currently those two are so noisy in my codebase that it is not worth investigating further.
This was also reported here: #75
Rule: no-used-vars
There are a couple of things causing false negatives with no-unused-vars
:
(1) Class properties created via the constructor, and then later used in a method
class SomeClass {
constructor(
private foo: string
) {}
someMethod() {
return this.foo
}
}
/* ESLint:
error, 'foo' is defined but never used
(2) Decorators
import { Injectable } from 'foo-framework'
@Injectable()
class Service {}
/* ESLint:
error, 'Injectable' is defined but never used
This was also reported here: #55
Rule: no-useless-constructor
Using the same class property assignment within a constructor example from above, we will also get a false negative on no-useless-constructor
class SomeClass {
constructor(
private foo: string
) {}
someMethod() {
return this.foo
}
}
/* ESLint:
error, Useless constructor
Rule: space-infix-ops
This rule is basically unusable, given the type annotation syntax :
Rule: semi
It was pointed out here #61 (comment) that TypeScript-only statements will currently not be detectable as needing a semi-colon.
E.g.
"semi": ["error", "always"]
type Result<T> = Success<T> | Failure
/* ESLint:
(No error reported, but should point out missing semi-colon)
Activity
JamesHenry commentedon Sep 1, 2016
I would also like to point out that in spite of the above, I am running over 100 of the core ESLint rules on the large TypeScript codebase without any problems at all!
🎉
nzakas commentedon Sep 2, 2016
Thanks for doing this! I'm low on energy, so probably will dig in more next week.
Off the top of my head, I think we can make
space-infix-ops
skip type annotations in the core rule (we've done this elsewhere).For
semi
, are there many TS-specific constructs that need semicolons? Or is it just a subset?JamesHenry commentedon Sep 6, 2016
No problem at all! Just reply whenever you can.
I cannot say that I have ever thought about it (I personally use no semi-colons, regardless of JavaScript or TypeScript), but I feel like it would be limited to a subset.
I will look into submitting a PR to eslint for the
space-infix-ops
rule.nzakas commentedon Sep 6, 2016
Part of what I'm wondering is if things like the
type
statement can be represented asVariableDeclaration
so it would transparently work, but if there's a large number of statements besides that, then we need something a bit more generic.JamesHenry commentedon Sep 6, 2016
There is no reference to semi-colons in the language spec: https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md
I have asked the TypeScript team here: microsoft/TypeScript#10730
JamesHenry commentedon Sep 7, 2016
@nzakas It seems like it might be worth doing it for just the type alias (e.g.
type Result<T> = Success<T> | Failure
) initially:From Mohamed on the TS Team:
So, coming back to your suggestion:
That would be an update to the core rule right? Not an AST (and therefore parser) change?
nzakas commentedon Sep 8, 2016
@JamesHenry what I meant was, we change what the parser output so that the type alias declaration outputs a
VariableDeclaration
node instead of whatever we're doing now. That way, the core rule will just work because it already checksVariableDeclaration
nodes (though we'd have to double-check that other core rules aren't confused by this, but giving it a brief once-over, I think it should work fine).Basically,
VariableDeclaration.kind
would be"type"
instead of"var"
or"let"
or"const"
.JamesHenry commentedon Sep 8, 2016
Ah gotcha! Yeah let's give that a go as a first step
JamesHenry commentedon Sep 8, 2016
🎉
New: Convert type alias into variable declaration (refs #77)
vitorbal commentedon Sep 8, 2016
This is great, awesome job @JamesHenry!
jkillian commentedon Sep 11, 2016
@JamesHenry nice work on the type aliases, I think treating them as a special kind of variable declaration makes sense - since it's very similar syntacticly.
Not sure if you have a list of TS-specific constructs you're testing, but a couple more worth checking if they need special treatment:
Module declarations, for example shorthand ambient module declarations:
Type guards:
The non-null assertion operator:
Typed usages of
this
(this
is not really a parameter):I don't think any of these will cause too big of issues, and some of them are relatively new TS features, but wanted to throw them out there as things to watch out for!
32 remaining items
JamesHenry commentedon Dec 11, 2017
This issue is very outdated and potentially confusing to new users so I am going to close it. Please feel free to open new issues for anything which you feel has not been addressed yet.
scottohara commentedon Dec 11, 2017
@JamesHenry is it worth filing a new issue for
no-useless-constructor
/no-empty-function
; as I believe these are still outstanding (see example below).Or is this something more appropriately handled by
eslint-plugin-typescript
? (similar to howtypescript/no-unused-vars
is used to correct any spurious unused vars warnings)For now, I have turned off these two rules in my
.ts
projects; but for projects that mix*.ts
and*.js
code, it would be nice to be able to have these rules enabled to catch any useless constructors/empty functions in*.js
code without it also warning about valid*.ts
constructors.Example:
Edit: Created as #418
nevir commentedon Dec 11, 2017
Typescript itself now tests for unused member properties, so for
.ts
you probably want to leverage that (but it won't catch empty constructors, I suppose).I think you can conditionally turn on the rule for
.js
files - see https://eslint.org/docs/user-guide/configuring#configuration-based-on-glob-patterns(impl: eslint/eslint#8081 and eslint/eslint#7177)
scottohara commentedon Dec 11, 2017
Sure, but this issue was originally about ESLint rules "just working" with TS-nodes; and clearly that's not the case yet for
no-useless-constructor
/no-empty-function
.I don't think configuring glob patterns to workaround this satisfies the original intent of "just works".
nevir commentedon Dec 12, 2017
no-undef
with mixed JS & TS codebase #416tchakabam commentedon Dec 19, 2017
@JamesHenry I have created a seperate issue about the problem in mixed codebases with the
no-undef
rule, for which we have a workaround, but not a fix yet :)#416
no-useless-constructor
/no-empty-function
with mixed JS & TS codebase #418keplersj commentedon Jan 30, 2018
I apologize if this comment creates unnecessary noise but I'm a little confused about the status of this issue. Is this warning in the readme still accurate?
tchakabam commentedon Jan 31, 2018
@keplersj It is still true for
no-undef
:) which is why issue #416 has been opened.However, if your codebase is pure Typescript, this isn't likely to be a real problem. You can disable that rule, as the ts compiler will shout anyway if anything is undef :)
keplersj commentedon Jan 31, 2018
@tchakabam Thanks for clearing that up for me. 😁 I've sent a PR to update the README but haven't followed commit guidelines correctly. I'll fix that. #440
ffxsam commentedon Sep 10, 2018
Anyone know what the status of this is? I'm running into the issue where ESLint will throw
no-unused-vars
when importing TypeScript interfaces, even though they're being used.