TypeScript Version: 2.3.2
Code
import _ = require('lodash')
// with 'noImplicitAny' compiler option and without '@types/lodash' package dependency
Desired behavior:
The compiler emits no error.
Actual behavior:
The compiler emits this error:
Could not find a declaration file for module 'lodash'. '/project/node_modules/lodash/lodash.js' implicitly has an 'any' type.
Motivation:
noImplicitAny turned on for my own code.@types packages, which for many JS packages do not exist. (And unfortunately some of the ones that do exist are poorly maintained.).d.ts files for every non-typed third-party JS package I need to use.More docs available at http://www.typescriptlang.org/docs/handbook/modules.html#shorthand-ambient-modules
Thanks. From those resources, the recommendation seems to be "Add one file to your project that contains a declare module 'xyz' line for each module xyz you want to import as any."
This is not ideal, but it works. I'd rather not have the extra file, and I'd especially rather not have to add a line to that file for each any module I need, but it's not as invasive as I feared from my first readings of related issue threads and the above-linked resources over the past few days.
Still, it would be much better to have the compiler option. But if it's already been reviewed and decided against by the team, that's fair.
There's also declare module "*"; that will make unknown modules any by default.
Thanks, I missed that. I thought it was going to make all modules any.
However, with declare module '*' I see that I lose module resolution's ability to provide an error when it can't find the module. This moves a very convenient compile-time check to a potentially confusing set of one or more runtime errors.
What's the rationale against this compiler option? That this issue was tagged "Question" instead of "Suggestion" when I was aiming for the latter implies one exists, I think.
From the discussions I've read in the issues, and from the current lack of a usage pattern that is both non-invasive and exactly equivalent, it seems like the rationale is that its benefit to the user doesn't outweigh the cost to develop it. But if that were true, this should be tagged "Suggestion" instead of "Question", since I'm assuming one measure of user benefit is how many users indicate they would benefit from it.
Would something in TS break or become inconsistent if this compiler option existed?
Could someone please remove the Question label and add the Suggestion label?
I'm asking because this issue was drafted as a suggestion, not as a question, and the suggested functionality is not fully provided by the (still very helpful) workarounds given by the team here and in other issues' discussions.
For other TS users out there who (a) have encountered this same deficiency in TS, (b) want this compiler option, and (c) are browsing and searching the issues, the labeling should indicate that this issue is a valid suggestion --- unless it isn't, but that hasn't been explained (or even asserted).
Thanks.
Marking as suggestion. but i do not think we will be doing this any time soon.
The compiler has a default behavior of treating all imports with no associated declaration files as any. this allows you to skip the definition file search and acquisition step. But it is rightfully flagged under --noImplicitAny.
Allowing the any's from module imports to propagate defeats the whole point of using --noImplicitAny. --noImplicitAny at its core gives you safe refactoring guarantees; this is built on the fact that the compiler knows the types of everything explicitly. allowing module imports to leak implicit anys into your program breaks that assumption.
The language also has affordances for declaring modules, so you can declare specific modules that you do not have declaration files for as declare module "jQuery"; or as @andy-ms mentioned earlier go all the way to declare module "*";. So you already have 1. treat it as any if it exists, or 2. trust me I know what i am doing, having yet another third state adds complexity, reduces safety and not sure i see it adding much value.
@mhegazy Thanks for replying (and re-labeling).
Allowing the any's from module imports to propagate defeats the whole point of using
--noImplicitAny.--noImplicitAnyat its core gives you safe refactoring guarantees; this is built on the fact that the compiler knows the types of everything explicitly. allowing module imports to leak implicitanys into your program breaks that assumption.
This isn't in doubt at all. I'm with you 100% on this.
However, when you use an externally packaged module that is _simply not_ actually (or correctly) typed, you have _no choice_ but to deal with any and its leakages. However, that fact _should not_ prevent you from using the benefits of noImplicitAny _in the rest of your code_.
The good news is that we have declare module, as you and @andy-ms have already explained (which I appreciate immensely).
The bad news is
declare module '*' causes TS to not flag missing modules as errors, anddeclare module 'xyz' for each any-typed module xyz is verbose.I feel like we're talking past each other and that this really comes down to a disagreement over whether "bad news 2" is actually bothersome.
However, when you use an externally packaged module that is simply not actually (or correctly) typed, you have no choice but to deal with any and its leakages.
this seems like one or n modules, and hopefully not all modules you deal with all the time ; and for that adding a declare module "foo"; seems like an appropriate fix untill you have the time and desire to write foo.d.ts.
However, that fact should not prevent you from using the benefits of noImplicitAny in the rest of your code.
what i am arguing here is that you are not using the benefits of noImplcitAny here. the flag's value is not that most of your code has types, it is that all your code has types.
declare module 'xyz'for each any-typed modulexyzis verbose.
and this is the intention. the idea is you made a conscious decision to treat some modules as any, and you are aware of the implications.
the flag's value is not that _most_ of your code has types, it is that _all_ your code has types.
I think I'll have to defer to your experience on this one, because I can only offer the single data point of my own project as counter-evidence. My colleagues and I have definitely benefited from noImplicitAny even though we use a few any-typed modules. The leakage of any has been easy to isolate where it occurs.
I agree that the workarounds are appropriate and not usually a big bother. From my own viewpoint though, they leave a small gap where there's actually a valuable user story. I have to admit I can't argue that the value is non-negligible at this point, though.
Thank you both for the help and insight.
the flag's value is not that _most_ of your code has types, it is that _all_ your code has types.
I think that's too strong of a statement, the flag is just about preventing implicit anys.
What seems strange to me is that variables and consts can be marked with any easily while imported symbols need this quite unintuitive solution with a new .d.ts file and declare module '...' for each untyped package in it.
For reference, import foo: any from "module"; has been proposed in https://github.com/microsoft/typescript/issues/3019#issuecomment-98851781. I'm not saying this specific syntax is great but it's conceptually much closer to what I'd expect. Also the allowImplicitAnyImports compiler option proposed here makes sense to me.
For me, it comes down to usability: in the world of various npm modules, untyped imports are quite common and it's currently not intuitive how to deal with it _easily_. I've seen people just doing require() instead of import which is a strange workaround and maybe suggests that there is some slight usability problem in TypeScript in this area.
Most helpful comment
I think that's too strong of a statement, the flag is just about preventing implicit
anys.What seems strange to me is that variables and consts can be marked with
anyeasily while imported symbols need this quite unintuitive solution with a new.d.tsfile anddeclare module '...'for each untyped package in it.For reference,
import foo: any from "module";has been proposed in https://github.com/microsoft/typescript/issues/3019#issuecomment-98851781. I'm not saying this specific syntax is great but it's conceptually much closer to what I'd expect. Also theallowImplicitAnyImportscompiler option proposed here makes sense to me.For me, it comes down to usability: in the world of various npm modules, untyped imports are quite common and it's currently not intuitive how to deal with it _easily_. I've seen people just doing
require()instead ofimportwhich is a strange workaround and maybe suggests that there is some slight usability problem in TypeScript in this area.