Hi again,
I am not sure if this is issue or design decision, but I can see conflict between rules no-inferrebale-types
and typedef
.
Ex.
function fn(): void {
for (let i = 0; i < 100; i++) {
console.log(i);
}
}
Snippet from https://github.com/palantir/tslint/blob/master/docs/sample.tslint.json:
"typedef": [true, ...],
"no-inferrable-types": true,
When I have these two rules turned on, I get:
error (typedef) test.ts[2, 12]: expected variable-declaration: 'i' to have a typedef
Adding type annotation number
to variable i
causes in turn following error:
error (no-inferrable-types) test.ts[2, 14]: LHS type (number) inferred by RHS expression, remove type annotation
Is there any way to have these two rules coexisted with each other?
For example, I want to have inferrable variables directly declared with primitive type (as rule doc says: number
, boolean
or string
), but on the other hand, I want to force typedefs on non-primitive types.
Thanks,
O.
Good catch, thanks for the heads-up. I added the no-inferrable-types
rule without thinking about how it might conflict with the typedef
rule, whoops! For now I'd recommend turning one of the two off, or only using some options of the typedef
rule.
Longer term, I think we'll either want to integrate no-inferrable-types
into the typedef
rule or we'll want to at least have TSLint detect conflicting configurations like having both rules on.
I am facing the same issue.
I've turned off no-inferrable-types for now.
Yes same here, I would like to have ability to have both the rules co-exist.
I would not want typedef rule to kick in if the variable's type can be inferred.
i.e. "no-inferrable-types" should have priority over "typedef"
+1
let id: number = 0;
for (let job: string of NAMES_PROFFESSIONS) {
/** some code */
id++;
}
(no-inferrable-types) LHS type (number) inferred by RHS expression, remove type annotation
+1
Does your definition of "inferrable" types include constructor assignments?
// BAD (this hurts my eyes to read)
let labels: Map<string, string> = new Map<string, string>();
// GOOD (type is obvious)
let labels = new Map<string, string>();
but also...
// BAD (in a diff, it's not obvious what this type is)
let labels = this.buildLabels();
// GOOD
let labels: Map<string, string> = this.buildLabels();
Yes, it's dangerous. If I want to simplify my code and prevent to use type declaration for directly initialized variables, I can't do this strictly and this brings to such thing:
let x = 2;
let y;
let z: number;
x = 1;
y = 1;
z = 1;
x = 's'; // Type 'string' is not assignable to type 'number'
y = 's'; // It's OK
z = 's'; // Type 'string' is not assignable to type 'number'
It's may be a very useful option to allow skip type declaration only for initialized variables.
… and really not only for primitive types, as @pgonzal says!
Look at this, it's terrible:
const onChange: () => void = () => this.update();
:+1: Ideally (imo) I would like a way to say "always require a typedef, unless the type is inferrable". Which I don't think is possible right now.
I ran into this and made an ignore-params
flag that helps out in my case. I want to force typedefs for all method/function parameters even when they can be easily inferred.
See PR: #1190 if you want to try it out
It's been a while since the original issue was submitted. Is the recommend option at this point to disable no-inferrable-types
and include the type on everything? Anything else to try and enable and combine both no-inferrable-types
and typedef
seems like a hack and results in a bunch of pointless warnings. Hoping for a better solution in the near future.
@corydeppen Note the 8 thumbs up on @englercj's suggestion, above. It's unclear what "combine" means in your "seems like a hack" comment. If you mean "use together in tslint.json
, then, yes. But "combining" an optional-inferrable-types
argument on typedef
would be great (at least, for 9 of us).
Can we get an update on this, please?
I think the best way to handle this is to deprecate the no-inferrable-types
and just pass an option object to typedef
to ignore the lack of type definitions if the type is inferrable according to certain patters, as initialized
, initialized primitives
, call signatures
and other patters that would fulfill our needs as developers.
For me this makes more sense, cause there should be always a typedef, unless there is something telling you what it's the type of the function. And it would be configurable as well, because, maybe we want the initialized properties
in a class to have inferrable type, but not for the call signatures
for example.
It would be great if someone could pitch in to get this fixed. Until then, we are forced to choose between "not enough type declarations to be readable" versus "cluttered with too many type declarations".
This issue has been open since Oct 3, 2015 -- since then, my team has authored around 2000 TypeScript source files that are all cluttered with too many type declarations.
The typedef
accepts a configuration. So maybe just another configuration just to ignore the type definition if the type is inferrable, meaning
Just type wherever is not being initialized.
It looks like because there is no clear consensus on how to deal with the issue, no progress is being made. Example comment: https://github.com/theia-ide/theia/issues/356#issuecomment-319350833
I suspect we all agree that any solution is better than leaving things as is. If you think any change to the status-quo with respect to this issue is good, please 👍 this comment. If you're knowledgeable enough to create a PR to fix this issue, please help all of us out ❤️ .
Would accept a PR to:
typedef
that lets it ignore cases where no-inferrable-types
says not to provide a typeThe typedef accepts a configuration. So maybe just another configuration just to ignore the type definition if the type is inferrable, meaning 'Just type wherever is not being initialized.'
The typedef
rule is for people who like having explicit type definitions in their codebase. If you desire the above behavior to "just type wherever is not being initialized", you're better off disabling typedef
and making sure you have noImplictAny
enabled as a TypeScript compiler option.
There's also the tricky case where some things that are initialized need a typedef anyways, as in the following snippet:
interface Literal {
field: "value"
}
const literal0 = {
field: "value",
};
const literal1: Literal = {
field: "value",
};
const func = (obj: Literal) => { };
func(literal0); // Error! Type 'string' is not assignable to type '"value"'.
func(literal1);
Also, while we get this fixed, wanted to mention that there are likely some great 3rd-party rules out there, like no-unnecessary-type-annotation
(https://github.com/ajafff/tslint-consistent-codestyle/blob/master/docs/no-unnecessary-type-annotation.md) for example. If anyone knows of any other 3rd-party rules that give the desired behavior, please post them here and we can officially recommend them or adopt them into core if it makes sense.
@JKillian thanks for the recommendation, I think that's actually what I wanted. There is a very good post about avoiding any
type: Don't use "naked any", create an "any interface" instead.
About:
interface Literal {
field: "value"
}
const literal0 = {
field: "value",
};
const literal1: Literal = {
field: "value",
};
const func = (obj: Literal) => { };
func(literal0); // Error! Type 'string' is not assignable to type '"value"'.
func(literal1);
I don't see how this could be an undesired behavior nor a tricky case. You want to make sure that obj
have a property field
with value value
, and even when you are initializing literal0
with a property that seems like the constrains, you could modify that to another string.
I know is not a good use case, but most of the cases when you are using a literal, you probably want that literal, not a primitive.
I have the following configuration:
json
"no-inferrable-types": true,
"typedef": [true, "call-signature", "parameter"],
And this code:
javascript
private static readonly DEVICE_UID: string = 'device_uid';
private static readonly DEVICE_PLATFORM: string = 'browser';
private static readonly AGENT_DEFAULT_ICON = 'http://localhost:3000/icon.png';
Why I'm not getting error in the two first declarations?
@sandrocsimas Interesting, but off-topic I think; AFAICT that problem's unrelated to this issue. I'd suggest you start another issue (fwiw!).
@estaub , yes I will. I'm getting the same behavior even without the typedef rule.
@sandrocsimas that's because it's a readonly property, and such Typescript infer its type as a literal. Typing it as a string you are telling that it should have a string, it does not necessarily will have that literal value and the value should not change statically.
It would be nice to have a 'require-typedef-except-inferrable' rule.
@FiretronP75 as @JKillian said, that's just noImplicitAny
option of the TSC.
@michaeljota thanks, I didn't realize the noImplicitAny
option of the compiler gives exceptions for inferrable. It still would be nice to have in tslint though, for the option of making it a warning instead of breaking compile, and for having the tslint comment flags.
I see why this would be something wanted, but having no-unused-variables
as an example, I don't think use cases covered by the TSC are going to be supported by the TSLint team. I know that is not the same an _linter error_ than a _compiler error_ but at the end, they both are about written better code. Now days with solutions as Webpack or Parcel that allows you to compile and run the code even with TSC errors, I don't see this as a real issue.
Has this been fixed in the latest version?
I still don't think this is on the roadmap. You should consider using noImplicitAny
from the TSC
☠️ TSLint's time has come! ☠️
TSLint is no longer accepting most feature requests per #4534. See typescript-eslint.io for the new, shiny way to lint your TypeScript code with ESLint. ✨
It was a pleasure open sourcing with you all!
🤖 Beep boop! 👉 TSLint is deprecated 👈 _(#4534)_ and you should switch to typescript-eslint! 🤖
🔒 This issue is being locked to prevent further unnecessary discussions. Thank you! 👋
Most helpful comment
:+1: Ideally (imo) I would like a way to say "always require a typedef, unless the type is inferrable". Which I don't think is possible right now.