How to configure TypeScript to throw an error/warning for incorrect import statement? - javascript

I am working on converting a large application from JavaScript (Backbone and Angular 1) to TypeScript. When we convert a file that is used by other files we understand that we have to update the import statements in those other JavaScript files so that it imports the new TypeScript file correctly. Our syntax update in fake-file.js is as follows.
Before:
import OurService from 'our.service';
After:
import { OurService } from 'our.service';
I understand that this is an easy change but TypeScript is new to many developers and there have been problems with people missing some of these import statements or forgetting to change them all together resulting in some issues during runtime. I have looked into compiler options but I do not see any that would fix this issue but I could be misinterpreting them.
Question: Is there a way to configure the compiler (or a Visual Studio Code plugin) to throw a warning or an error to prevent this from happening?

I assume that I understood your requirement and possibly you need to adapt a linting process and consequently I would suggest the following tools (which I also use in my project):
Airbnb Javascript style guide (your import statement concern-https://github.com/airbnb/javascript#modules). These are a well-defined set of standards defined for any JS application (including ES).
ESLint. You can run ESLint from the terminal and configure it for your project that highlights warning/errors in your code. If this looks complicated, you can generate the tslint document for your entire project in the website itself. Click on rules configuration and configure the ES rules for your project. There are some import related rules too.
PS: Feel free to add your comments.

Related

Change the Default File Resolution in JavaScript

In a JavaScript project, a file titled index.js can be imported as such:
import SomeComponent from 'components/some-component'
Rather than having to specify index.js.
In my project, I prefer to use a different naming convention: some-component.component.js. This way I can tell what the file is from a glance (rather than having a million index.js).
What I'm trying to achieve is having this same import pattern happen for files with the pattern *.component.js. In other words:
import SomeComponent from 'components/some-component'
Rather than
import SomeComponent from 'components/some-component/some-component.component.js'
I have the following (abbreviated) jsconfig.json:
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"*": ["./*"]
}
}
}
Any ideas on how this could be achieved?
Thanks in advance!
Short answer: you can't customize the import resolution using a function (or RegExp). For a web project, you may use a tool like Webpack with a custom loader. But, that is a Webpack specific solution that will not work with other tools (like doing Ctrl-click in VSCode, or using TSC directly).
Long answer:
The resolution of the JavaScript files depends on the tools you use. The standards changed a little bit over the years, and while things are converging to ESM there are still a lot of inconsistencies.
The TypeScript module resolution is described here: https://www.typescriptlang.org/docs/handbook/module-resolution.html
You can have a baseUrl, merge multiple directories as one (rootDirs), or have a limited wildcard path mapping (https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping). You already used it in your question, so probably I'm not telling you anything new. But, that's where the TS configuration options end. (you can customize the compilation module resolution using the TSC API, but not from the CLI/tools config options)
NodeJS can hook a resolution mechanism for modules. For example, Yarn 2+ does that to support a feature called PNP (https://yarnpkg.com/features/pnp). However, TypeScript still needs to resolve types and it will not use your custom loading mechanism for that.
The situation with other tools like Webpack is similar.
The module resolution of Node and TypeScript are specific to those tools. The ESM standard is even more strict: the from part must be an absolute or relative URL, or a "bare identifier" that you need to map using an import map. In other words, you can't have a custom algorithm to resolve paths. The justification for that is described in the Import Maps standard repo: https://github.com/WICG/import-maps#a-programmable-resolution-hook
Conclusion: if you really-really want to do a custom module resolution, you may be able to create your own CLI using the TSC API. That may not work with other tools, and is probably easier to get used to another naming convention.

How to use npm packages as ES6 modules loaded by the browser in 2021?

I'm a JavaScript beginner. I do not use a "bundler".
For a few days now I've been trying to use moment.js and other date-time libraries in some JavaScript by importing it using ES6 modules (ESM).
I have a JS module I wrote, which is transpiled from TS, and that has this line:
import moment from "../lib/moment/src/moment.js"
My module is itself loaded via an import from a <script type="module" > tag, which works.
I've discovered that the moment package contains what looks like "source" code in its src folder which seems to look more like the JS/TS I'm accustomed to, and has a "default export" at the bottom.
So I'm referencing that "source" version in my transpiled JS module. This gets me past a couple of errors I was getting:
The requested module 'blah' does not provide an export named 'default'
And
Cannot set property 'moment' of undefined (at moment.js:10)
And leaves me stuck with loading the other modules its dependent upon, because I guess, their file extensions are missing.
GET https://blah/lib/moment/src/lib/locale/locale net::ERR_ABORTED 404 (moment.js:37)
After 3 days tearing my hair out I feel like I have been fighting a battle I shouldn't be attempting at all.
I would expect in 2021, what with widespread ESM browser support, that this would just work, but I've tried 5 different date-time libraries and had no success.
I assume the 404s have occurred because the moment authors are NOT expecting people to be using their library like this, so they left off the file extensions knowing full well that it wouldn't load in a browser??
Am I supposed to add an extra step in my client-side build process, Gulp, to add the extensions back on in the moment source code??
Or am I doing something else wrong?
Is it perhaps that everyone uses a "bundler" and these tools fix all this stuff somehow and so these issues never arise for 99% of web devs??
Thanks in advance.
You want to import a bundled version of the lib to be able to do that. Try:
import from 'https://unpkg.com/moment#2.29.1/dist/moment.js' ;
You can download the bundled version and add to your project. Don't forget to put the license as well and check if they are at least MIT or similar.
If you want to refer to the source code you will certainly need to build that. Specifically with libs that are using typescript.

how import works in modules imported into Angular

The documentation I've read is rather hand-wavy about what exactly import does in Javascript, particularly in the Angular framework. I get that it imports modules from another file that has one or more exports. But there are many permutations of its syntax, and not all are discussed with much detail. I'm currently having a very hard time with the #asymmetik/ngx-leaflet-markercluster module. When I try to compile my Angular app, I get a message reading "Can't resolve 'leaflet.markercluster' in 'C:\sca_root\city8\node_modules#asymmetrik\ngx-leaflet-markercluster\dist\leaflet-markercluster" -- this is in reference to a line that reads simply
import 'leaflet.markercluster';
This seems to me (and I know, perhaps I am making too many assumptions here) that there should be a file in that same directory named leaflet.markercluster.js or perhaps leaflet.markercluster.ts (it's Javascript, not TypeScript, so it will be the former). But there is no file named leaflet.markercluster.js in that directory. These are the files in that directory:
leaflet-markercluster.directive.js.map
leaflet-markercluster.directive.metadata.json
leaflet-markercluster.module.d.ts
leaflet-markercluster.module.js
leaflet-markercluster.module.js.map
leaflet-markercluster.module.metadata.json
leaflet-markercluster.directive.d.ts
eaflet-markercluster.directive.js
Which one would that import statement import? If not any of them, where outside this directory would it import that file from? What other information (perhaps in tsconfig.json or angular.json) might affect where this import statement imports from?
the problem in this case was that leaflet.markercluster ALSO needs #types installed. i had to issue a
npm install #types/leaflet #types/leaflet.markercluster
i don't know why the error had nothing to do with #types, but this is in keeping with the soul-crushing nature of Angular development.

Visual Studio 2017, JavaScript intellisense inconsistencies

TL;DR: How do you include a tsconfig.json file in Visual Studio 2017 and still have JavaScript intellisense work like it does by default, giving code hints both for your own code and 3rd-party libraries? Is it borked, or do I just need a better understanding of how it works?
Much like jQuery, I would like ubiquitous access to my own JavaScript namespaces across JavaScript and html files with intellisense. How does one achieve this?
EDIT 3:
I reported this issue on developercommunity.visualstudio.com. Please consider up-voting the issue to attract more attention from Microsoft.
EDIT 2:
Currently the "solution" is Resharper, which provides what one would expect straight out-of-the-box. But it's not really ideal at a price of $299/yr/user for something Visual Studio markets it provides itself.
EDIT 1:
In the event that my expectations for JavaScript intellisense in Visual Studio is inaccurate, below is a contrived example of how I expect it to function.
Say I create three TypeScript files in my ./Scripts folder, we'll call them A.ts, B.ts, and C.ts. Each will contain its own namespace:
A.ts
namespace A {
export function f1(s: string) {
return s;
}
}
B.ts
namespace B {
export function f1(n: number) {
return n;
}
}
C.ts
namespace C {
export function f1(b: boolean) {
return b;
}
}
At this point in time one may expect to be able to start seeing intellisense for their newly created namespaces, and indeed, within A.ts, B.ts, and C.ts you begin to see intellisense for those namespaces. Cool.
Unfortunately this doesn't carry over to other places, like /Home/Index.cshtml. Index.cshtml knows nothing about the A, B, or C namespaces. Additionally, .js files know nothing about these namespaces, hence the need for their respective TypeScript Declaration (.d.ts) files.
So we'll go ahead and add our tsconfig.json file to the root of our project and configure it like we do above. If we perform an edit on one of our .ts files and save it, this triggers the compilation of our corresponding .d.ts files into our ./Scripts/out folder. Do we now get intellisense in Index.cshtml and .js files? ...No.
Now you might be thinking these files need to be included in the project in order for Visual Studio to pick them up for intellisense (they're excluded on creation). So you include them in the project. Intellisense? No. jQuery intellisense? No. Perhaps they need to be in the same directory as the file you want to use them in? .....Kind of? I've ran into intellisense being inconsistent like this on a number of occassions:
Somehow we're getting intellisense for our 'A' namespace within our C3.js (note: a JavaScript file), but none of the other ones. What?
Perhaps you think you're on the right track and that you've scored a small victory against the intellisense engine. But then you restart your Visual Studio instance and...
Wait....I just had intellisense for the 'A' namespace. What happened!?
I have no idea. But after messing around for a bit I try placing an import statement at the top of the .js file, and all of a sudden intellisense starts kicking in for the imported files/modules. Huzzah!
And even in Index.cshtml I begin receiving intellisense for our imported namespaces.
The moment I close the .js file with the import statements, though, all of a sudden I lose the intellisense for them in Index.cshtml. Wait...what?? And unlike in JavaScript files, I'm unable to write an import statement to receive intellisense for them:
Or at least... not with my own namespaces:
It's this inconsistent back-and-forth that confuses the heck out of me as to how exactly Visual Studio's JavaScript intellisense actually works. I still don't know if it's just broken or if I need a better understanding of it.
ORIGINAL POST
System
Windows 10VS2017 15.0.0+26228.10New JavaScript language service is enabled
I've been fighting to get Visual Studio 2017's JavaScript intellisense to work for me for a couple of days now to no avail. I don't know if something's genuinely wonky with the IDE or if I just don't understand it properly.
My understanding of how VS2017's JS intellisense works is now by typescript definition files, or .d.ts files. Ultimately I would like to get intellisense to help us out with our own TypeScript/JavaScript but right now I'd settle for just having it work consistently within a fresh project.
If I create a new ASP.NET MVC 4.5.2 project, out-of-the-box JS intellisense seems to work fine, and by that I mean I can type a dollar sign ($) either in a .js file or between <script> tags and I properly receive intellisense for jQuery. Awesome. Now, with the ultimate goal in mind of creating TypeScript files (.ts) and having the TypeScript compiler generate our .d.ts files automagically so that we can get rich intellisense for our own code, I wish to introduce a tsconfig.json file to the root of the project in order to configure that.
After creating the tsconfig.json file JS intellisense ceases to function. I type a dollar sign ($) into either a .js file or between <script> tags and I get zero intellisense for jQuery. This was a frustrating experience for a while but then I read this little note found on the linked page above:
NOTE: This feature is disabled by default if using a tsconfig.json
configuration file, but may be set to enabled as outlined further
below).
"This feature" is in reference to "By default, the Salsa language service will try to detect which JavaScript libraries are in use...in order to provide richer IntelliSense." Ok, so the Salsa language service will not autodetect libraries in use and provide intellisense for them when a tsconfig.json file is present, unless you specifically configure it to do so as "outlined below." At least, that's how I understand it.
If we scroll down the page a little bit and come to the tsconfig file settings we come across a setting for enableAutoDiscovery which "enables the automatic detection and download of definition files as outlined above." It's not super explicit as to what "as outlined above" is in reference to, but I can only assume it's in reference to the previous note indicative of re-enabling Salsa's auto-detection feature to provide rich intellisense. So I include that option in my tsconfig.json file and...no benefit; still no JS intellisense.
As it turns out, typingOptions.enableAutoDiscovery has been renamed to typeAcquisition.enable. Grrrrr. Alright, so we make that change in our tsconfig.json aaaaaaand... still no JS intellisense.
At this point I am able to write my own .d.ts files and Visual Studio's intellisense will pick it up alright (most of the time...), but I still have no intellisense for 3rd-party libraries like jQuery, which is no good.
So far I've landed on the following tsconfig.json file:
{
"compilerOptions": {
"noEmitOnError": true,
"noImplicitAny": false,
"removeComments": false,
"target": "es5",
"declaration": true,
"declarationDir": "./Scripts/out"
},
"compileOnSave": true,
"typeAcquisition": {
"enable": true,
"include": ["jquery"]
},
"include": [
"./Scripts/app/**/*"
],
"exclude": [
"./Scripts/vendors/**/*",
"./Scripts/out/**/*"
]
}
With the TypeScript virtual project in view I can see that my project's .ts files are included as well as jQuery in my %LOCALAPPDATA%\Microsoft\TypeScript\node_modules#types directory.
I was hopeful that with all this in place I would have both intellisense for my own project's .ts files and 3rd-party libraries (at least those specifically included; jQuery only at this point). Alas...no. Still no jQuery intellisense.
Incidentally, if I write an import line for jQuery, all of a sudden I get jQuery intellisense
...interesting. And maybe that makes perfect sense; I haven't messed a whole lot with module imports and how Visual Studio's intellisense handles those, but what I really want is to have intellisense application-wide for both our code and 3rd-party libraries.
JS files are for javascript and are not transpiled using the typescript compiler.
If you are using typescript then keep all your typescript in ts files.
Also, you shouldn't put typescript inside your cshtml files because typescript is not javascript. You need to use the typescript compiler to transpile your typescript into javascript which you can then use that javascript inside your HTML pages.
I think what you are trying to do is not a way typically how people use typescript. Usually you write all your typescript in TS files and then as a build process you transpile all your typescript into javascript. There are many strategies on how to do this. You can bundle all your javascript into one file or break them up into modules. There is a lot of options on how you want your final javascript output to look.
Your CSHTML views would reference your compiled javascript and not your typescript. The browser doesn't know what to do w/ typescript.
It looks like the latest update March 14, 2017 (build 26228.09) caused this issue, you can go to Control Panel—Programs and Features-View Installed updates and remove this update, then reopen VS to check the intellisense. If it works, which means this update lead to this issue, you can temporarily not install this updat
I've noticed the Microsoft Report Projects for Visual Studio Extension can cause problems such as this when being used in a project, A couple of mine have broken right after I add my SQL Server Report project to the solution and restarted visual studio.. So for now I remove my Report project from the solution but I hope Microsoft fixes this issue

How export and import work in typescript?

I was going through Angular2 quickstart tutorial with Javascript and Typescript as well, In javascript version I observed that components and modules are first assigned to a variable (window.app which I understood as some global variable that can be accessed across js files or script blocks) and that is fine. Coming to type script version just export and import were used, I tried to analyze the generated javascript code but understood nothing. Can some one explain me how this export and import works in Tyepescript.
Import and export in typescript are explained well by the documentation here https://www.typescriptlang.org/docs/handbook/modules.html.
Like toskv said in his comment, how those statements in your TypeScript files get transpiled into statements in your JavaScript files depends largely on the module system you set up in your tsconfig.json file.
For example, setting "module": "commonjs" will cause the TypeScript compiler (tsc) to transform your import/export statements into essentially node.js-style require() statements. This documentation has a few simple, but helpful, examples of how node.js modules work: https://nodejs.org/api/modules.html.
Using a setting of "systemjs" instead of "commonjs" will make TypeScript translate your import/export statements into a format that SystemJS understands, of which I am no expert.
This process is further complicated by the fact that Angular 2 projects also require build steps that take the transpiled JavaScript files and turn them into packaged "bundles." These bundled files are (depending on your configuration settings) concatenated, minified, and perhaps even uglified. So looking at the final javascript code that is run is really not helpful, as it was not written by humans.
For example, the Webpack build system (google webpack.js) takes require() statements it finds in JavaScript code and does some magic to wrap each module in its own __webpack_require__ function, which allows the build system to take your whole project file structure and bundle it in to one or several JavaScript files which still maintain their dependencies on each other.
In other words, by the time you look at the production JavaScript code, it's not meant to be intelligible by human readers. The flow can be simply represented by TS Source Code > TS Transpilation into JS Code > Module/Dependency Build Steps into Production JS Code.
TL;DR TypeScript doesn't actually handle the module importing/exporting. During transpilation, it converts those statements into statements other module systems (node.js or SystemJS) can understand, which are in turn converted into production code for serving an Angular 2 application.

Categories

Resources